diff options
-rw-r--r-- | source3/include/nameserv.h | 1 | ||||
-rw-r--r-- | source3/include/proto.h | 7 | ||||
-rw-r--r-- | source3/nmbd/nmbd.c | 10 | ||||
-rw-r--r-- | source3/nmbd/nmbd_become_lmb.c | 29 | ||||
-rw-r--r-- | source3/nmbd/nmbd_incomingdgrams.c | 151 | ||||
-rw-r--r-- | source3/nmbd/nmbd_packets.c | 56 | ||||
-rw-r--r-- | source3/nmbd/nmbd_processlogon.c | 2 | ||||
-rw-r--r-- | source3/nmbd/nmbd_sendannounce.c | 106 | ||||
-rw-r--r-- | source3/param/loadparm.c | 10 |
9 files changed, 359 insertions, 13 deletions
diff --git a/source3/include/nameserv.h b/source3/include/nameserv.h index 4b7216fef6..98a6cb330a 100644 --- a/source3/include/nameserv.h +++ b/source3/include/nameserv.h @@ -122,6 +122,7 @@ enum netbios_reply_type_code { NMB_QUERY, NMB_STATUS, NMB_REG, NMB_REG_REFRESH, #define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE" #define NET_LOGON_MAILSLOT "\\MAILSLOT\\NET\\NETLOGON" #define NT_LOGON_MAILSLOT "\\MAILSLOT\\NET\\NTLOGON" +#define LANMAN_MAILSLOT "\\MAILSLOT\\LANMAN" /* Samba definitions for find_name_on_subnet(). */ #define FIND_ANY_NAME 0 diff --git a/source3/include/proto.h b/source3/include/proto.h index b249c9cb20..a3fd1d1f0f 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -298,6 +298,8 @@ int lp_lpqcachetime(void); int lp_syslog(void); int lp_client_code_page(void); int lp_announce_as(void); +int lp_lm_announce(void); +int lp_lm_interval(void); char *lp_preexec(int ); char *lp_postexec(int ); char *lp_rootpreexec(int ); @@ -485,6 +487,7 @@ void unbecome_local_master_fail(struct subnet_record *subrec, struct response_re void release_1d_name( struct subnet_record *subrec, char *workgroup_name); void unbecome_local_master_browser(struct subnet_record *subrec, struct work_record *work); void become_local_master_browser(struct subnet_record *subrec, struct work_record *work); +void set_workgroup_local_master_browser_name( struct work_record *work, char *newname); /*The following definitions come from nmbd_browserdb.c */ @@ -517,11 +520,13 @@ void process_workgroup_announce(struct subnet_record *subrec, struct packet_stru void process_local_master_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf); void process_master_browser_announce(struct subnet_record *subrec, struct packet_struct *p,char *buf); +void process_lm_host_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf); void process_get_backup_list_request(struct subnet_record *subrec, struct packet_struct *p,char *buf); void process_reset_browser(struct subnet_record *subrec, struct packet_struct *p,char *buf); void process_announce_request(struct subnet_record *subrec, struct packet_struct *p, char *buf); +void process_lm_announce_request(struct subnet_record *subrec, struct packet_struct *p, char *buf); /*The following definitions come from nmbd_incomingrequests.c */ @@ -670,6 +675,7 @@ void reply_netbios_packet(struct packet_struct *orig_packet, int ttl, char *data,int len); void queue_packet(struct packet_struct *packet); void process_browse_packet(struct packet_struct *p, char *buf,int len); +void process_lanman_packet(struct packet_struct *p, char *buf,int len); BOOL validate_nmb_response_packet( struct nmb_packet *nmb ); BOOL validate_nmb_packet( struct nmb_packet *nmb ); void run_packet_queue(); @@ -706,6 +712,7 @@ struct response_record *find_response_record(struct subnet_record **ppsubrec, void send_browser_reset(int reset_type, char *to_name, int to_type, struct in_addr to_ip); void broadcast_announce_request(struct subnet_record *subrec, struct work_record *work); void announce_my_server_names(time_t t); +void announce_my_lm_server_names(time_t t); void reset_announce_timer(); void announce_myself_to_domain_master_browser(time_t t); void announce_my_servers_removed(void); diff --git a/source3/nmbd/nmbd.c b/source3/nmbd/nmbd.c index 11cd50cd76..86f01d8e79 100644 --- a/source3/nmbd/nmbd.c +++ b/source3/nmbd/nmbd.c @@ -47,6 +47,9 @@ extern char **my_netbios_names; /* are we running as a daemon ? */ static BOOL is_daemon = False; +/* have we found LanMan clients yet? */ +BOOL found_lm_clients = False; + /* what server type are we currently */ time_t StartupTime = 0; @@ -289,6 +292,13 @@ static void process(void) announce_my_server_names(t); /* + * Send out any LanMan broadcast announcements + * of our server names. + * (nmbd_sendannounce.c) + */ + announce_my_lm_server_names(t); + + /* * If we are a local master browser, periodically * announce ourselves to the domain master browser. * This also deals with syncronising the domain master diff --git a/source3/nmbd/nmbd_become_lmb.c b/source3/nmbd/nmbd_become_lmb.c index 7f54471a24..6496a2f9e5 100644 --- a/source3/nmbd/nmbd_become_lmb.c +++ b/source3/nmbd/nmbd_become_lmb.c @@ -115,7 +115,7 @@ in workgroup %s on subnet %s\n", /* Forget who the local master browser was for this workgroup. */ - *work->local_master_browser_name = '\0'; + set_workgroup_local_master_browser_name( work, ""); /* * Ensure the IP address of this subnet is not registered as one @@ -333,8 +333,7 @@ on subnet %s\n", work->work_group, subrec->subnet_name)); subrec->work_changed = True; /* Add this name to the workgroup as local master browser. */ - StrnCpy(work->local_master_browser_name, myname, - sizeof(work->local_master_browser_name)-1); + set_workgroup_local_master_browser_name( work, myname); /* Count the number of servers we have on our list. If it's less than 10 (just a heuristic) request the servers @@ -532,3 +531,27 @@ in workgroup %s on subnet %s\n", become_local_master_fail1, userdata); } + +/*************************************************************** + Utility function to set the local master browser name. Does + some sanity checking as old versions of Samba seem to sometimes + say that the master browser name for a workgroup is the same + as the workgroup name. +****************************************************************/ + +void set_workgroup_local_master_browser_name( struct work_record *work, char *newname) +{ + DEBUG(5,("set_workgroup_local_master_browser_name: setting local master name to '%s' \ +for workgroup %s.\n", newname, work->work_group )); + + if(strequal( work->work_group, newname)) + { + DEBUG(5, ("set_workgroup_local_master_browser_name: Refusing to set \ +local_master_browser_name for workgroup %s to workgroup name.\n", + work->work_group )); + return; + } + + StrnCpy(work->local_master_browser_name, newname, + sizeof(work->local_master_browser_name)-1); +} diff --git a/source3/nmbd/nmbd_incomingdgrams.c b/source3/nmbd/nmbd_incomingdgrams.c index d43b1369e6..452516b64e 100644 --- a/source3/nmbd/nmbd_incomingdgrams.c +++ b/source3/nmbd/nmbd_incomingdgrams.c @@ -28,6 +28,7 @@ extern int DEBUGLEVEL; extern pstring myname; extern fstring myworkgroup; +extern BOOL found_lm_clients; #if 0 @@ -223,9 +224,7 @@ void process_workgroup_announce(struct subnet_record *subrec, struct packet_stru if(*work->local_master_browser_name == '\0') { /* Set the master browser name. */ - StrnCpy(work->local_master_browser_name, master_name, - sizeof(work->local_master_browser_name)-1); - + set_workgroup_local_master_browser_name( work, master_name ); } subrec->work_changed = True; @@ -324,9 +323,7 @@ a local master browser for workgroup %s and we think we are master. Forcing elec StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1); } - /* Set the master browser name. */ - StrnCpy(work->local_master_browser_name, server_name, - sizeof(work->local_master_browser_name)-1); + set_workgroup_local_master_browser_name( work, server_name ); subrec->work_changed = True; } @@ -384,6 +381,105 @@ master - ignoring master announce.\n")); update_browser_death_time(browrec); } +/******************************************************************* + Process an incoming LanMan host announcement packet. +*******************************************************************/ + +void process_lm_host_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + uint32 servertype = IVAL(buf,1); + int osmajor=CVAL(buf,5); /* major version of node software */ + int osminor=CVAL(buf,6); /* minor version of node software */ + int ttl = SVAL(buf,7); + char *announce_name = buf+9; + struct work_record *work; + struct server_record *servrec; + char *work_name; + char *source_name = dgram->source_name.name; + pstring comment; + char *s = buf+9; + + s = skip_string(s,1); + StrnCpy(comment, s, 43); + + DEBUG(3,("process_lm_host_announce: LM Announcement from %s<%02x> IP %s to \ +%s for server %s.\n", source_name, source_name[15], inet_ntoa(p->ip), + namestr(&dgram->dest_name),announce_name)); + + DEBUG(5,("process_lm_host_announce: os=(%d,%d) ttl=%d server type=%08x comment=%s\n", + osmajor, osminor, ttl, servertype,comment)); + + if ((osmajor < 36) || (osmajor > 38) || (osminor !=0)) + { + DEBUG(5,("process_lm_host_announce: LM Announcement packet does not " \ + "originate from OS/2 Warp client. Ignoring packet.\n")); + /* Could have been from a Windows machine (with its LM Announce enabled), + or a Samba server. Then don't disrupt the current browse list. */ + return; + } + + /* Filter servertype to remove impossible bits. */ + servertype &= ~(SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM); + + /* A LanMan host announcement must be sent to the name WORKGROUP<00>. */ + if(dgram->dest_name.name_type != 0x00) + { + DEBUG(2,("process_lm_host_announce: incorrect name type for destination from IP %s \ +(was %02x) should be 0x00. Allowing packet anyway.\n", + inet_ntoa(p->ip), dgram->dest_name.name_type)); + /* Change it so it was. */ + dgram->dest_name.name_type = 0x00; + } + + /* For a LanMan host announce the workgroup name is the destination name. */ + work_name = dgram->dest_name.name; + + /* + * Syntax servers version 5.1 send HostAnnounce packets to + * *THE WRONG NAME*. They send to LOCAL_MASTER_BROWSER_NAME<00> + * instead of WORKGROUP<1d> name. So to fix this we check if + * the workgroup name is our own name, and if so change it + * to be our primary workgroup name. This code is probably + * not needed in the LanMan announce code, but it won't hurt. + */ + + if(strequal(work_name, myname)) + work_name = myworkgroup; + + /* + * We are being very agressive here in adding a workgroup + * name on the basis of a host announcing itself as being + * in that workgroup. Maybe we should wait for the workgroup + * announce instead ? JRA. + */ + + if ((work = find_workgroup_on_subnet(subrec, work_name))==NULL) + { + /* We have no record of this workgroup. Add it. */ + if((work = create_workgroup_on_subnet(subrec, work_name, ttl))==NULL) + return; + } + + if((servrec = find_server_in_workgroup( work, announce_name))==NULL) + { + /* If this server is not already in the workgroup, add it. */ + create_server_on_workgroup(work, announce_name, + servertype|SV_TYPE_LOCAL_LIST_ONLY, + ttl, comment); + } + else + { + /* Update the record. */ + servrec->serv.type = servertype|SV_TYPE_LOCAL_LIST_ONLY; + update_server_ttl( servrec, ttl); + StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1); + } + + subrec->work_changed = True; + found_lm_clients = True; +} + /**************************************************************************** Send a backup list response. *****************************************************************************/ @@ -600,12 +696,12 @@ request from %s IP %s state=0x%X\n", } /******************************************************************* - Process a announcement request packet. + Process an announcement request packet. We don't respond immediately, we just check it's a request for - out workgroup and then set the flag telling the announce code + our workgroup and then set the flag telling the announce code in nmbd_sendannounce.c:announce_my_server_names that an announcement is needed soon. - ******************************************************************/ +******************************************************************/ void process_announce_request(struct subnet_record *subrec, struct packet_struct *p, char *buf) { @@ -634,3 +730,40 @@ void process_announce_request(struct subnet_record *subrec, struct packet_struct work->needannounce = True; } + +/******************************************************************* + Process a LanMan announcement request packet. + We don't respond immediately, we just check it's a request for + our workgroup and then set the flag telling that we have found + a LanMan client (DOS or OS/2) and that we will have to start + sending LanMan announcements (unless specifically disabled + through the "lm_announce" parameter in smb.conf) +******************************************************************/ + +void process_lm_announce_request(struct subnet_record *subrec, struct packet_struct *p, char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + struct work_record *work; + char *workgroup_name = dgram->dest_name.name; + + DEBUG(3,("process_lm_announce_request: Announce request from %s IP %s to %s.\n", + namestr(&dgram->source_name), inet_ntoa(p->ip), + namestr(&dgram->dest_name))); + + /* We only send announcement requests on our workgroup. */ + if(strequal(workgroup_name, myworkgroup) == False) + { + DEBUG(7,("process_lm_announce_request: Ignoring announce request for workgroup %s.\n", + workgroup_name)); + return; + } + + if((work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL) + { + DEBUG(0,("process_announce_request: Unable to find workgroup %s on subnet !\n", + workgroup_name)); + return; + } + + found_lm_clients = True; +} diff --git a/source3/nmbd/nmbd_packets.c b/source3/nmbd/nmbd_packets.c index 43249cc0a3..4fb0543967 100644 --- a/source3/nmbd/nmbd_packets.c +++ b/source3/nmbd/nmbd_packets.c @@ -1024,6 +1024,56 @@ command code %d from %s IP %s to %s\n", } /**************************************************************************** + Dispatch a LanMan browse frame from port 138 to the correct processing function. +****************************************************************************/ + +void process_lanman_packet(struct packet_struct *p, char *buf,int len) +{ + struct dgram_packet *dgram = &p->packet.dgram; + int command = SVAL(buf,0); + struct subnet_record *subrec = find_subnet_for_dgram_browse_packet(p); + + /* Drop the packet if it's a different NetBIOS scope, or + the source is from one of our names. */ + + if (!strequal(dgram->dest_name.scope,scope )) + { + DEBUG(7,("process_lanman_packet: Discarding datagram from IP %s. Scope (%s) \ +mismatch with our scope (%s).\n", inet_ntoa(p->ip), dgram->dest_name.scope, scope)); + return; + } + + if (is_myname(dgram->source_name.name)) + { + DEBUG(0,("process_lanman_packet: Discarding datagram from IP %s. Source name \ +%s is one of our names !\n", inet_ntoa(p->ip), namestr(&dgram->source_name))); + return; + } + + switch (command) + { + case ANN_HostAnnouncement: + { + debug_browse_data(buf, len); + process_lm_host_announce(subrec, p, buf+1); + break; + } + case ANN_AnnouncementRequest: + { + process_lm_announce_request(subrec, p, buf+1); + break; + } + default: + { + DEBUG(0,("process_lanman_packet: On subnet %s ignoring browse packet \ +command code %d from %s IP %s to %s\n", + subrec->subnet_name, command, namestr(&dgram->source_name), + inet_ntoa(p->ip), namestr(&dgram->dest_name))); + } + } +} + +/**************************************************************************** Determine if a packet is for us on port 138. Note that to have any chance of being efficient we need to drop as many packets as possible at this stage as subsequent processing is expensive. @@ -1100,6 +1150,12 @@ static void process_dgram(struct packet_struct *p) return; } + /* Datagram packet received for the LAN Manager mailslot */ + if (strequal(smb_buf(buf),LANMAN_MAILSLOT)) { + process_lanman_packet(p,buf2,len); + return; + } + /* Datagram packet received for the domain logon mailslot */ if (strequal(smb_buf(buf),NET_LOGON_MAILSLOT)) { diff --git a/source3/nmbd/nmbd_processlogon.c b/source3/nmbd/nmbd_processlogon.c index ae917564fe..cd2fbfd0a5 100644 --- a/source3/nmbd/nmbd_processlogon.c +++ b/source3/nmbd/nmbd_processlogon.c @@ -63,7 +63,7 @@ void process_logon_packet(struct packet_struct *p,char *buf,int len, if (!lp_domain_logons()) { - DEBUG(3,("process_logon_packet: Logon packet received from IP %S and domain \ + DEBUG(3,("process_logon_packet: Logon packet received from IP %s and domain \ logons are not enabled.\n", inet_ntoa(p->ip) )); return; } diff --git a/source3/nmbd/nmbd_sendannounce.c b/source3/nmbd/nmbd_sendannounce.c index e4b288aea5..aac3dad366 100644 --- a/source3/nmbd/nmbd_sendannounce.c +++ b/source3/nmbd/nmbd_sendannounce.c @@ -32,6 +32,7 @@ extern pstring myname; extern fstring myworkgroup; extern char **my_netbios_names; extern int updatecount; +extern BOOL found_lm_clients; /**************************************************************************** Send a browser reset packet. @@ -127,6 +128,37 @@ static void send_announcement(struct subnet_record *subrec, int announce_type, } /**************************************************************************** + Broadcast a LanMan announcement. +**************************************************************************/ + +static void send_lm_announcement(struct subnet_record *subrec, int announce_type, + char *from_name, char *to_name, int to_type, struct in_addr to_ip, + time_t announce_interval, + char *server_name, int server_type, char *server_comment) +{ + pstring outbuf; + char *p=outbuf; + + bzero(outbuf,sizeof(outbuf)); + + SSVAL(p,0,announce_type); + SIVAL(p,2,server_type & ~SV_TYPE_LOCAL_LIST_ONLY); + CVAL(p,6) = lp_major_announce_version(); /* Major version. */ + CVAL(p,7) = lp_minor_announce_version(); /* Minor version. */ + SSVAL(p,8,announce_interval); /* In seconds - according to spec. */ + + p += 10; + StrnCpy(p,server_name,15); + strupper(p); + p = skip_string(p,1); + pstrcpy(p,server_comment); + p = skip_string(p,1); + + send_mailslot(False,LANMAN_MAILSLOT, outbuf, PTR_DIFF(p,outbuf), + from_name, 0x0, to_name, to_type, to_ip, subrec->myip); +} + +/**************************************************************************** We are a local master browser. Announce this to WORKGROUP<1e>. ****************************************************************************/ @@ -192,6 +224,29 @@ static void send_host_announcement(struct subnet_record *subrec, struct work_rec } /**************************************************************************** + Announce the given LanMan host +****************************************************************************/ + +static void send_lm_host_announcement(struct subnet_record *subrec, struct work_record *work, + struct server_record *servrec, int lm_interval) +{ + /* Ensure we don't have the prohibited bits set. */ + uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY; + + DEBUG(3,("send_lm_host_announcement: type %x for host %s on subnet %s for workgroup %s, ttl: %d\n", + type, servrec->serv.name, subrec->subnet_name, work->work_group, lm_interval)); + + send_lm_announcement(subrec, ANN_HostAnnouncement, + servrec->serv.name, /* From nbt name. */ + work->work_group, 0x00, /* To nbt name. */ + subrec->bcast_ip, /* To ip. */ + lm_interval, /* Time until next announce. */ + servrec->serv.name, /* Name to announce. */ + type, /* Type field. */ + servrec->serv.comment); +} + +/**************************************************************************** Announce a server record. ****************************************************************************/ @@ -258,6 +313,56 @@ void announce_my_server_names(time_t t) } /* for subrec */ } +/**************************************************************************** + Go through all my registered names on all broadcast subnets and announce + them as a LanMan server if the timeout requires it. +**************************************************************************/ + +void announce_my_lm_server_names(time_t t) +{ + struct subnet_record *subrec; + static time_t last_lm_announce_time=0; + int announce_interval = lp_lm_interval(); + int lm_announce = lp_lm_announce(); + + if ((announce_interval <= 0) || (lm_announce <= 0)) + { + /* user absolutely does not want LM announcements to be sent. */ + return; + } + + if ((lm_announce >= 2) && (!found_lm_clients)) + { + /* has been set to 2 (Auto) but no LM clients detected (yet). */ + return; + } + + /* Otherwise: must have been set to 1 (Yes), or LM clients *have* + been detected. */ + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + struct work_record *work = find_workgroup_on_subnet(subrec, myworkgroup); + + if(work) + { + struct server_record *servrec; + + if (last_lm_announce_time && ((t - last_lm_announce_time) < announce_interval )) + continue; + + last_lm_announce_time = t; + + for (servrec = work->serverlist; servrec; servrec = servrec->next) + { + if (is_myname(servrec->serv.name)) + /* skipping equivalent of announce_server() */ + send_lm_host_announcement(subrec, work, servrec, announce_interval); + } + } /* if work */ + } /* for subrec */ +} + /* Announce timer. Moved into global static so it can be reset when a machine becomes a local master browser. */ static time_t announce_timer_last=0; @@ -342,6 +447,7 @@ void announce_my_servers_removed(void) if(AM_LOCAL_MASTER_BROWSER(work)) send_local_master_announcement(subrec, work, servrec); send_host_announcement(subrec, work, servrec); + send_lm_host_announcement(subrec, work, servrec, 0); } } } diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index 76618e9a79..277430ecc4 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -160,6 +160,8 @@ typedef struct int max_wins_ttl; int min_wins_ttl; int ReadSize; + int lm_announce; + int lm_interval; int shmem_size; int client_code_page; int announce_as; /* This is initialised in init_globals */ @@ -409,6 +411,8 @@ static struct enum_list enum_announce_as[] = {{ANNOUNCE_AS_NT, "NT"}, {ANNOUNCE_ static struct enum_list enum_case[] = {{CASE_LOWER, "lower"}, {CASE_UPPER, "upper"}, {-1, NULL}}; +static struct enum_list enum_lm_announce[] = {{0, "False"}, {1, "True"}, {2, "Auto"}}; + static struct parm_struct { char *label; @@ -507,6 +511,8 @@ static struct parm_struct {"max ttl", P_INTEGER, P_GLOBAL, &Globals.max_ttl, NULL, NULL}, {"max wins ttl", P_INTEGER, P_GLOBAL, &Globals.max_wins_ttl, NULL, NULL}, {"min wins ttl", P_INTEGER, P_GLOBAL, &Globals.min_wins_ttl, NULL, NULL}, + {"lm announce", P_ENUM, P_GLOBAL, &Globals.lm_announce, NULL, enum_lm_announce}, + {"lm interval", P_INTEGER, P_GLOBAL, &Globals.lm_interval, NULL, NULL}, {"dns proxy", P_BOOL, P_GLOBAL, &Globals.bDNSproxy, NULL, NULL}, {"wins support", P_BOOL, P_GLOBAL, &Globals.bWINSsupport, NULL, NULL}, {"wins proxy", P_BOOL, P_GLOBAL, &Globals.bWINSproxy, NULL, NULL}, @@ -699,6 +705,8 @@ static void init_globals(void) Globals.max_wins_ttl = 60*60*24*3; /* 3 days default */ Globals.min_wins_ttl = 60*60*6; /* 6 hours default */ Globals.ReadSize = 16*1024; + Globals.lm_announce = 2; /* = Auto: send only if LM clients found */ + Globals.lm_interval = 60; Globals.shmem_size = SHMEM_SIZE; Globals.announce_as = ANNOUNCE_AS_NT; Globals.bUnixRealname = False; @@ -947,6 +955,8 @@ FN_GLOBAL_INTEGER(lp_lpqcachetime,&Globals.lpqcachetime) FN_GLOBAL_INTEGER(lp_syslog,&Globals.syslog) FN_GLOBAL_INTEGER(lp_client_code_page,&Globals.client_code_page) FN_GLOBAL_INTEGER(lp_announce_as,&Globals.announce_as) +FN_GLOBAL_INTEGER(lp_lm_announce,&Globals.lm_announce) +FN_GLOBAL_INTEGER(lp_lm_interval,&Globals.lm_interval) FN_LOCAL_STRING(lp_preexec,szPreExec) FN_LOCAL_STRING(lp_postexec,szPostExec) |