/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Main startup code for SMB/NETBIOS and some utility routines * for the NETBIOS layer. */ #include #include #include #include #include #include #include #include #include #include #include #include #define SMB_NETBIOS_DUMP_FILE "netbios" static netbios_service_t nbtd; static void smb_netbios_shutdown(void); static void *smb_netbios_service(void *); static void smb_netbios_dump(void); /* * Start the NetBIOS services */ int smb_netbios_start(void) { pthread_t tid; pthread_attr_t attr; int rc; if (smb_netbios_cache_init() < 0) return (-1); (void) pthread_attr_init(&attr); (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); rc = pthread_create(&tid, &attr, smb_netbios_service, NULL); (void) pthread_attr_destroy(&attr); return (rc); } /* * Stop the NetBIOS services */ void smb_netbios_stop(void) { char fname[MAXPATHLEN]; smb_netbios_event(NETBIOS_EVENT_STOP); (void) snprintf(fname, MAXPATHLEN, "%s/%s", SMB_VARRUN_DIR, SMB_NETBIOS_DUMP_FILE); (void) unlink(fname); } /* * Launch the NetBIOS Name Service, Datagram and Browser services * and then sit in a loop providing a 1 second resolution timer. * The timer will: * - update the netbios stats file every 10 minutes * - clean the cache every 10 minutes */ /*ARGSUSED*/ static void * smb_netbios_service(void *arg) { static uint32_t ticks = 0; pthread_t tid; int rc; smb_netbios_event(NETBIOS_EVENT_START); rc = pthread_create(&tid, NULL, smb_netbios_name_service, NULL); if (rc != 0) { smb_netbios_shutdown(); return (NULL); } smb_netbios_wait(NETBIOS_EVENT_NS_START); if (smb_netbios_error()) { smb_netbios_shutdown(); return (NULL); } smb_netbios_name_config(); rc = pthread_create(&tid, NULL, smb_netbios_datagram_service, NULL); if (rc != 0) { smb_netbios_shutdown(); return (NULL); } smb_netbios_wait(NETBIOS_EVENT_DGM_START); if (smb_netbios_error()) { smb_netbios_shutdown(); return (NULL); } rc = pthread_create(&tid, NULL, smb_browser_service, NULL); if (rc != 0) { smb_netbios_shutdown(); return (NULL); } smb_netbios_event(NETBIOS_EVENT_TIMER_START); for (;;) { (void) sleep(1); ticks++; if (!smb_netbios_running()) break; smb_netbios_datagram_tick(); smb_netbios_name_tick(); if ((ticks % 600) == 0) { smb_netbios_event(NETBIOS_EVENT_DUMP); smb_netbios_cache_clean(); } } smb_netbios_event(NETBIOS_EVENT_TIMER_STOP); smb_netbios_shutdown(); return (NULL); } static void smb_netbios_shutdown(void) { (void) pthread_join(nbtd.nbs_browser.s_tid, 0); (void) pthread_join(nbtd.nbs_dgm.s_tid, 0); (void) pthread_join(nbtd.nbs_ns.s_tid, 0); nbtd.nbs_browser.s_tid = 0; nbtd.nbs_dgm.s_tid = 0; nbtd.nbs_ns.s_tid = 0; smb_netbios_cache_fini(); if (smb_netbios_error()) { smb_netbios_event(NETBIOS_EVENT_RESET); if (smb_netbios_start() != 0) syslog(LOG_ERR, "netbios: restart failed"); } } int smb_first_level_name_encode(struct name_entry *name, unsigned char *out, int max_out) { return (netbios_first_level_name_encode(name->name, name->scope, out, max_out)); } int smb_first_level_name_decode(unsigned char *in, struct name_entry *name) { return (netbios_first_level_name_decode((char *)in, (char *)name->name, (char *)name->scope)); } /* * smb_encode_netbios_name * * Set up the name and scope fields in the destination name_entry structure. * The name is padded with spaces to 15 bytes. The suffix is copied into the * last byte, i.e. "netbiosname ". The scope is copied and folded * to uppercase. */ void smb_encode_netbios_name(unsigned char *name, char suffix, unsigned char *scope, struct name_entry *dest) { smb_tonetbiosname((char *)name, (char *)dest->name, suffix); if (scope) { (void) strlcpy((char *)dest->scope, (const char *)scope, sizeof (dest->scope)); } else { (void) smb_config_getstr(SMB_CI_NBSCOPE, (char *)dest->scope, sizeof (dest->scope)); } (void) smb_strupr((char *)dest->scope); } void smb_init_name_struct(unsigned char *name, char suffix, unsigned char *scope, uint32_t ipaddr, unsigned short port, uint32_t attr, uint32_t addr_attr, struct name_entry *dest) { bzero(dest, sizeof (struct name_entry)); smb_encode_netbios_name(name, suffix, scope, dest); switch (smb_node_type) { case 'H': dest->attributes = attr | NAME_ATTR_OWNER_TYPE_HNODE; break; case 'M': dest->attributes = attr | NAME_ATTR_OWNER_TYPE_MNODE; break; case 'P': dest->attributes = attr | NAME_ATTR_OWNER_TYPE_PNODE; break; case 'B': default: dest->attributes = attr | NAME_ATTR_OWNER_TYPE_BNODE; break; } dest->addr_list.refresh_ttl = dest->addr_list.ttl = TO_SECONDS(DEFAULT_TTL); dest->addr_list.sin.sin_family = AF_INET; dest->addr_list.sinlen = sizeof (dest->addr_list.sin); dest->addr_list.sin.sin_addr.s_addr = ipaddr; dest->addr_list.sin.sin_port = port; dest->addr_list.attributes = addr_attr; dest->addr_list.forw = dest->addr_list.back = &dest->addr_list; } void smb_netbios_event(netbios_event_t event) { static char *event_msg[] = { "startup", "shutdown", "restart", "name service started", "name service stopped", "datagram service started", "datagram service stopped", "browser service started", "browser service stopped", "timer service started", "timer service stopped", "error", "dump" }; (void) mutex_lock(&nbtd.nbs_mtx); if (event == NETBIOS_EVENT_DUMP) { if (nbtd.nbs_last_event == NULL) nbtd.nbs_last_event = event_msg[event]; smb_netbios_dump(); (void) mutex_unlock(&nbtd.nbs_mtx); return; } nbtd.nbs_last_event = event_msg[event]; syslog(LOG_DEBUG, "netbios: %s", nbtd.nbs_last_event); switch (nbtd.nbs_state) { case NETBIOS_STATE_INIT: if (event == NETBIOS_EVENT_START) nbtd.nbs_state = NETBIOS_STATE_RUNNING; break; case NETBIOS_STATE_RUNNING: switch (event) { case NETBIOS_EVENT_NS_START: nbtd.nbs_ns.s_tid = pthread_self(); nbtd.nbs_ns.s_up = B_TRUE; break; case NETBIOS_EVENT_NS_STOP: nbtd.nbs_ns.s_up = B_FALSE; break; case NETBIOS_EVENT_DGM_START: nbtd.nbs_dgm.s_tid = pthread_self(); nbtd.nbs_dgm.s_up = B_TRUE; break; case NETBIOS_EVENT_DGM_STOP: nbtd.nbs_dgm.s_up = B_FALSE; break; case NETBIOS_EVENT_BROWSER_START: nbtd.nbs_browser.s_tid = pthread_self(); nbtd.nbs_browser.s_up = B_TRUE; break; case NETBIOS_EVENT_BROWSER_STOP: nbtd.nbs_browser.s_up = B_FALSE; break; case NETBIOS_EVENT_TIMER_START: nbtd.nbs_timer.s_tid = pthread_self(); nbtd.nbs_timer.s_up = B_TRUE; break; case NETBIOS_EVENT_TIMER_STOP: nbtd.nbs_timer.s_up = B_FALSE; break; case NETBIOS_EVENT_STOP: nbtd.nbs_state = NETBIOS_STATE_CLOSING; break; case NETBIOS_EVENT_ERROR: nbtd.nbs_state = NETBIOS_STATE_ERROR; ++nbtd.nbs_errors; break; default: break; } break; case NETBIOS_STATE_CLOSING: case NETBIOS_STATE_ERROR: default: switch (event) { case NETBIOS_EVENT_NS_STOP: nbtd.nbs_ns.s_up = B_FALSE; break; case NETBIOS_EVENT_DGM_STOP: nbtd.nbs_dgm.s_up = B_FALSE; break; case NETBIOS_EVENT_BROWSER_STOP: nbtd.nbs_browser.s_up = B_FALSE; break; case NETBIOS_EVENT_TIMER_STOP: nbtd.nbs_timer.s_up = B_FALSE; break; case NETBIOS_EVENT_STOP: nbtd.nbs_state = NETBIOS_STATE_CLOSING; break; case NETBIOS_EVENT_RESET: nbtd.nbs_state = NETBIOS_STATE_INIT; break; case NETBIOS_EVENT_ERROR: ++nbtd.nbs_errors; break; default: break; } break; } smb_netbios_dump(); (void) cond_broadcast(&nbtd.nbs_cv); (void) mutex_unlock(&nbtd.nbs_mtx); } void smb_netbios_wait(netbios_event_t event) { boolean_t *svc = NULL; boolean_t desired_state; (void) mutex_lock(&nbtd.nbs_mtx); switch (event) { case NETBIOS_EVENT_NS_START: case NETBIOS_EVENT_NS_STOP: svc = &nbtd.nbs_ns.s_up; desired_state = (event == NETBIOS_EVENT_NS_START) ? B_TRUE : B_FALSE; break; case NETBIOS_EVENT_DGM_START: case NETBIOS_EVENT_DGM_STOP: svc = &nbtd.nbs_dgm.s_up; desired_state = (event == NETBIOS_EVENT_DGM_START) ? B_TRUE : B_FALSE; break; case NETBIOS_EVENT_BROWSER_START: case NETBIOS_EVENT_BROWSER_STOP: svc = &nbtd.nbs_browser.s_up; desired_state = (event == NETBIOS_EVENT_BROWSER_START) ? B_TRUE : B_FALSE; break; default: (void) mutex_unlock(&nbtd.nbs_mtx); return; } while (*svc != desired_state) { if (nbtd.nbs_state != NETBIOS_STATE_RUNNING) break; (void) cond_wait(&nbtd.nbs_cv, &nbtd.nbs_mtx); } (void) mutex_unlock(&nbtd.nbs_mtx); } void smb_netbios_sleep(time_t seconds) { timestruc_t reltimeout; (void) mutex_lock(&nbtd.nbs_mtx); if (nbtd.nbs_state == NETBIOS_STATE_RUNNING) { if (seconds == 0) seconds = 1; reltimeout.tv_sec = seconds; reltimeout.tv_nsec = 0; (void) cond_reltimedwait(&nbtd.nbs_cv, &nbtd.nbs_mtx, &reltimeout); } (void) mutex_unlock(&nbtd.nbs_mtx); } boolean_t smb_netbios_running(void) { boolean_t is_running; (void) mutex_lock(&nbtd.nbs_mtx); if (nbtd.nbs_state == NETBIOS_STATE_RUNNING) is_running = B_TRUE; else is_running = B_FALSE; (void) mutex_unlock(&nbtd.nbs_mtx); return (is_running); } boolean_t smb_netbios_error(void) { boolean_t error; (void) mutex_lock(&nbtd.nbs_mtx); if (nbtd.nbs_state == NETBIOS_STATE_ERROR) error = B_TRUE; else error = B_FALSE; (void) mutex_unlock(&nbtd.nbs_mtx); return (error); } /* * Write the service state to /var/run/smb/netbios. * * This is a private interface. To update the file use: * smb_netbios_event(NETBIOS_EVENT_DUMP); */ static void smb_netbios_dump(void) { static struct { netbios_state_t state; char *text; } sm[] = { { NETBIOS_STATE_INIT, "init" }, { NETBIOS_STATE_RUNNING, "running" }, { NETBIOS_STATE_CLOSING, "closing" }, { NETBIOS_STATE_ERROR, "error" } }; char fname[MAXPATHLEN]; FILE *fp; struct passwd *pwd; struct group *grp; uid_t uid; gid_t gid; char *last_event = "none"; int i; (void) snprintf(fname, MAXPATHLEN, "%s/%s", SMB_VARRUN_DIR, SMB_NETBIOS_DUMP_FILE); if ((fp = fopen(fname, "w")) == NULL) return; pwd = getpwnam("root"); grp = getgrnam("sys"); uid = (pwd == NULL) ? 0 : pwd->pw_uid; gid = (grp == NULL) ? 3 : grp->gr_gid; (void) lockf(fileno(fp), F_LOCK, 0); (void) fchmod(fileno(fp), 0600); (void) fchown(fileno(fp), uid, gid); if (nbtd.nbs_last_event) last_event = nbtd.nbs_last_event; for (i = 0; i < sizeof (sm) / sizeof (sm[0]); ++i) { if (nbtd.nbs_state == sm[i].state) { (void) fprintf(fp, "State %s (event: %s, errors: %u)\n", sm[i].text, last_event, nbtd.nbs_errors); break; } } (void) fprintf(fp, "Name Service %-7s (%u)\n", nbtd.nbs_ns.s_up ? "up" : "down", nbtd.nbs_ns.s_tid); (void) fprintf(fp, "Datagram Service %-7s (%u)\n", nbtd.nbs_dgm.s_up ? "up" : "down", nbtd.nbs_dgm.s_tid); (void) fprintf(fp, "Browser Service %-7s (%u)\n", nbtd.nbs_browser.s_up ? "up" : "down", nbtd.nbs_browser.s_tid); (void) fprintf(fp, "Timer Service %-7s (%u)\n", nbtd.nbs_timer.s_up ? "up" : "down", nbtd.nbs_timer.s_tid); smb_netbios_cache_dump(fp); (void) lockf(fileno(fp), F_ULOCK, 0); (void) fclose(fp); }