1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Main startup code for SMB/NETBIOS and some utility routines 28 * for the NETBIOS layer. 29 */ 30 31 #include <sys/tzfile.h> 32 #include <assert.h> 33 #include <synch.h> 34 #include <unistd.h> 35 #include <syslog.h> 36 #include <string.h> 37 #include <strings.h> 38 #include <sys/socket.h> 39 #include <stdio.h> 40 #include <pwd.h> 41 #include <grp.h> 42 #include <smbns_netbios.h> 43 44 #define SMB_NETBIOS_DUMP_FILE "netbios" 45 46 static netbios_service_t nbtd; 47 48 static void smb_netbios_shutdown(void); 49 static void *smb_netbios_service(void *); 50 static void smb_netbios_dump(void); 51 52 /* 53 * Start the NetBIOS services 54 */ 55 int 56 smb_netbios_start(void) 57 { 58 pthread_t tid; 59 pthread_attr_t attr; 60 int rc; 61 62 if (smb_netbios_cache_init() < 0) 63 return (-1); 64 65 (void) pthread_attr_init(&attr); 66 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 67 rc = pthread_create(&tid, &attr, smb_netbios_service, NULL); 68 (void) pthread_attr_destroy(&attr); 69 return (rc); 70 } 71 72 /* 73 * Stop the NetBIOS services 74 */ 75 void 76 smb_netbios_stop(void) 77 { 78 char fname[MAXPATHLEN]; 79 80 smb_netbios_event(NETBIOS_EVENT_STOP); 81 82 (void) snprintf(fname, MAXPATHLEN, "%s/%s", 83 SMB_VARRUN_DIR, SMB_NETBIOS_DUMP_FILE); 84 (void) unlink(fname); 85 86 } 87 88 /* 89 * Launch the NetBIOS Name Service, Datagram and Browser services 90 * and then sit in a loop providing a 1 second resolution timer. 91 * The timer will: 92 * - update the netbios stats file every 10 minutes 93 * - clean the cache every 10 minutes 94 */ 95 /*ARGSUSED*/ 96 static void * 97 smb_netbios_service(void *arg) 98 { 99 static uint32_t ticks = 0; 100 pthread_t tid; 101 int rc; 102 103 smb_netbios_event(NETBIOS_EVENT_START); 104 105 rc = pthread_create(&tid, NULL, smb_netbios_name_service, NULL); 106 if (rc != 0) { 107 smb_netbios_shutdown(); 108 return (NULL); 109 } 110 111 smb_netbios_wait(NETBIOS_EVENT_NS_START); 112 if (smb_netbios_error()) { 113 smb_netbios_shutdown(); 114 return (NULL); 115 } 116 117 smb_netbios_name_config(); 118 119 rc = pthread_create(&tid, NULL, smb_netbios_datagram_service, NULL); 120 if (rc != 0) { 121 smb_netbios_shutdown(); 122 return (NULL); 123 } 124 125 smb_netbios_wait(NETBIOS_EVENT_DGM_START); 126 if (smb_netbios_error()) { 127 smb_netbios_shutdown(); 128 return (NULL); 129 } 130 131 rc = pthread_create(&tid, NULL, smb_browser_service, NULL); 132 if (rc != 0) { 133 smb_netbios_shutdown(); 134 return (NULL); 135 } 136 137 smb_netbios_event(NETBIOS_EVENT_TIMER_START); 138 139 for (;;) { 140 (void) sleep(1); 141 ticks++; 142 143 if (!smb_netbios_running()) 144 break; 145 146 smb_netbios_datagram_tick(); 147 smb_netbios_name_tick(); 148 149 if ((ticks % 600) == 0) { 150 smb_netbios_event(NETBIOS_EVENT_DUMP); 151 smb_netbios_cache_clean(); 152 } 153 } 154 155 smb_netbios_event(NETBIOS_EVENT_TIMER_STOP); 156 smb_netbios_shutdown(); 157 return (NULL); 158 } 159 160 static void 161 smb_netbios_shutdown(void) 162 { 163 (void) pthread_join(nbtd.nbs_browser.s_tid, 0); 164 (void) pthread_join(nbtd.nbs_dgm.s_tid, 0); 165 (void) pthread_join(nbtd.nbs_ns.s_tid, 0); 166 167 nbtd.nbs_browser.s_tid = 0; 168 nbtd.nbs_dgm.s_tid = 0; 169 nbtd.nbs_ns.s_tid = 0; 170 171 smb_netbios_cache_fini(); 172 173 if (smb_netbios_error()) { 174 smb_netbios_event(NETBIOS_EVENT_RESET); 175 if (smb_netbios_start() != 0) 176 syslog(LOG_ERR, "netbios: restart failed"); 177 } 178 } 179 180 int 181 smb_first_level_name_encode(struct name_entry *name, 182 unsigned char *out, int max_out) 183 { 184 return (netbios_first_level_name_encode(name->name, name->scope, 185 out, max_out)); 186 } 187 188 int 189 smb_first_level_name_decode(unsigned char *in, struct name_entry *name) 190 { 191 return (netbios_first_level_name_decode((char *)in, (char *)name->name, 192 (char *)name->scope)); 193 } 194 195 /* 196 * smb_encode_netbios_name 197 * 198 * Set up the name and scope fields in the destination name_entry structure. 199 * The name is padded with spaces to 15 bytes. The suffix is copied into the 200 * last byte, i.e. "netbiosname <suffix>". The scope is copied and folded 201 * to uppercase. 202 */ 203 void 204 smb_encode_netbios_name(unsigned char *name, char suffix, unsigned char *scope, 205 struct name_entry *dest) 206 { 207 smb_tonetbiosname((char *)name, (char *)dest->name, suffix); 208 209 if (scope) { 210 (void) strlcpy((char *)dest->scope, (const char *)scope, 211 sizeof (dest->scope)); 212 } else { 213 (void) smb_config_getstr(SMB_CI_NBSCOPE, (char *)dest->scope, 214 sizeof (dest->scope)); 215 } 216 217 (void) utf8_strupr((char *)dest->scope); 218 } 219 220 void 221 smb_init_name_struct(unsigned char *name, char suffix, unsigned char *scope, 222 uint32_t ipaddr, unsigned short port, uint32_t attr, 223 uint32_t addr_attr, struct name_entry *dest) 224 { 225 bzero(dest, sizeof (struct name_entry)); 226 smb_encode_netbios_name(name, suffix, scope, dest); 227 228 switch (smb_node_type) { 229 case 'H': 230 dest->attributes = attr | NAME_ATTR_OWNER_TYPE_HNODE; 231 break; 232 case 'M': 233 dest->attributes = attr | NAME_ATTR_OWNER_TYPE_MNODE; 234 break; 235 case 'P': 236 dest->attributes = attr | NAME_ATTR_OWNER_TYPE_PNODE; 237 break; 238 case 'B': 239 default: 240 dest->attributes = attr | NAME_ATTR_OWNER_TYPE_BNODE; 241 break; 242 } 243 244 dest->addr_list.refresh_ttl = dest->addr_list.ttl = 245 TO_SECONDS(DEFAULT_TTL); 246 247 dest->addr_list.sin.sin_family = AF_INET; 248 dest->addr_list.sinlen = sizeof (dest->addr_list.sin); 249 dest->addr_list.sin.sin_addr.s_addr = ipaddr; 250 dest->addr_list.sin.sin_port = port; 251 dest->addr_list.attributes = addr_attr; 252 dest->addr_list.forw = dest->addr_list.back = &dest->addr_list; 253 } 254 255 void 256 smb_netbios_event(netbios_event_t event) 257 { 258 static char *event_msg[] = { 259 "startup", 260 "shutdown", 261 "restart", 262 "name service started", 263 "name service stopped", 264 "datagram service started", 265 "datagram service stopped", 266 "browser service started", 267 "browser service stopped", 268 "timer service started", 269 "timer service stopped", 270 "error", 271 "dump" 272 }; 273 274 (void) mutex_lock(&nbtd.nbs_mtx); 275 276 if (event == NETBIOS_EVENT_DUMP) { 277 if (nbtd.nbs_last_event == NULL) 278 nbtd.nbs_last_event = event_msg[event]; 279 smb_netbios_dump(); 280 (void) mutex_unlock(&nbtd.nbs_mtx); 281 return; 282 } 283 284 nbtd.nbs_last_event = event_msg[event]; 285 syslog(LOG_DEBUG, "netbios: %s", nbtd.nbs_last_event); 286 287 switch (nbtd.nbs_state) { 288 case NETBIOS_STATE_INIT: 289 if (event == NETBIOS_EVENT_START) 290 nbtd.nbs_state = NETBIOS_STATE_RUNNING; 291 break; 292 293 case NETBIOS_STATE_RUNNING: 294 switch (event) { 295 case NETBIOS_EVENT_NS_START: 296 nbtd.nbs_ns.s_tid = pthread_self(); 297 nbtd.nbs_ns.s_up = B_TRUE; 298 break; 299 case NETBIOS_EVENT_NS_STOP: 300 nbtd.nbs_ns.s_up = B_FALSE; 301 break; 302 case NETBIOS_EVENT_DGM_START: 303 nbtd.nbs_dgm.s_tid = pthread_self(); 304 nbtd.nbs_dgm.s_up = B_TRUE; 305 break; 306 case NETBIOS_EVENT_DGM_STOP: 307 nbtd.nbs_dgm.s_up = B_FALSE; 308 break; 309 case NETBIOS_EVENT_BROWSER_START: 310 nbtd.nbs_browser.s_tid = pthread_self(); 311 nbtd.nbs_browser.s_up = B_TRUE; 312 break; 313 case NETBIOS_EVENT_BROWSER_STOP: 314 nbtd.nbs_browser.s_up = B_FALSE; 315 break; 316 case NETBIOS_EVENT_TIMER_START: 317 nbtd.nbs_timer.s_tid = pthread_self(); 318 nbtd.nbs_timer.s_up = B_TRUE; 319 break; 320 case NETBIOS_EVENT_TIMER_STOP: 321 nbtd.nbs_timer.s_up = B_FALSE; 322 break; 323 case NETBIOS_EVENT_STOP: 324 nbtd.nbs_state = NETBIOS_STATE_CLOSING; 325 break; 326 case NETBIOS_EVENT_ERROR: 327 nbtd.nbs_state = NETBIOS_STATE_ERROR; 328 ++nbtd.nbs_errors; 329 break; 330 default: 331 break; 332 } 333 break; 334 335 case NETBIOS_STATE_CLOSING: 336 case NETBIOS_STATE_ERROR: 337 default: 338 switch (event) { 339 case NETBIOS_EVENT_NS_STOP: 340 nbtd.nbs_ns.s_up = B_FALSE; 341 break; 342 case NETBIOS_EVENT_DGM_STOP: 343 nbtd.nbs_dgm.s_up = B_FALSE; 344 break; 345 case NETBIOS_EVENT_BROWSER_STOP: 346 nbtd.nbs_browser.s_up = B_FALSE; 347 break; 348 case NETBIOS_EVENT_TIMER_STOP: 349 nbtd.nbs_timer.s_up = B_FALSE; 350 break; 351 case NETBIOS_EVENT_STOP: 352 nbtd.nbs_state = NETBIOS_STATE_CLOSING; 353 break; 354 case NETBIOS_EVENT_RESET: 355 nbtd.nbs_state = NETBIOS_STATE_INIT; 356 break; 357 case NETBIOS_EVENT_ERROR: 358 ++nbtd.nbs_errors; 359 break; 360 default: 361 break; 362 } 363 break; 364 } 365 366 smb_netbios_dump(); 367 (void) cond_broadcast(&nbtd.nbs_cv); 368 (void) mutex_unlock(&nbtd.nbs_mtx); 369 } 370 371 void 372 smb_netbios_wait(netbios_event_t event) 373 { 374 boolean_t *svc = NULL; 375 boolean_t desired_state; 376 377 (void) mutex_lock(&nbtd.nbs_mtx); 378 379 switch (event) { 380 case NETBIOS_EVENT_NS_START: 381 case NETBIOS_EVENT_NS_STOP: 382 svc = &nbtd.nbs_ns.s_up; 383 desired_state = 384 (event == NETBIOS_EVENT_NS_START) ? B_TRUE : B_FALSE; 385 break; 386 case NETBIOS_EVENT_DGM_START: 387 case NETBIOS_EVENT_DGM_STOP: 388 svc = &nbtd.nbs_dgm.s_up; 389 desired_state = 390 (event == NETBIOS_EVENT_DGM_START) ? B_TRUE : B_FALSE; 391 break; 392 case NETBIOS_EVENT_BROWSER_START: 393 case NETBIOS_EVENT_BROWSER_STOP: 394 svc = &nbtd.nbs_browser.s_up; 395 desired_state = 396 (event == NETBIOS_EVENT_BROWSER_START) ? B_TRUE : B_FALSE; 397 break; 398 default: 399 (void) mutex_unlock(&nbtd.nbs_mtx); 400 return; 401 } 402 403 while (*svc != desired_state) { 404 if (nbtd.nbs_state != NETBIOS_STATE_RUNNING) 405 break; 406 407 (void) cond_wait(&nbtd.nbs_cv, &nbtd.nbs_mtx); 408 } 409 410 (void) mutex_unlock(&nbtd.nbs_mtx); 411 } 412 413 void 414 smb_netbios_sleep(time_t seconds) 415 { 416 timestruc_t reltimeout; 417 418 (void) mutex_lock(&nbtd.nbs_mtx); 419 420 if (nbtd.nbs_state == NETBIOS_STATE_RUNNING) { 421 if (seconds == 0) 422 seconds = 1; 423 reltimeout.tv_sec = seconds; 424 reltimeout.tv_nsec = 0; 425 426 (void) cond_reltimedwait(&nbtd.nbs_cv, 427 &nbtd.nbs_mtx, &reltimeout); 428 } 429 430 (void) mutex_unlock(&nbtd.nbs_mtx); 431 } 432 433 boolean_t 434 smb_netbios_running(void) 435 { 436 boolean_t is_running; 437 438 (void) mutex_lock(&nbtd.nbs_mtx); 439 440 if (nbtd.nbs_state == NETBIOS_STATE_RUNNING) 441 is_running = B_TRUE; 442 else 443 is_running = B_FALSE; 444 445 (void) mutex_unlock(&nbtd.nbs_mtx); 446 return (is_running); 447 } 448 449 boolean_t 450 smb_netbios_error(void) 451 { 452 boolean_t error; 453 454 (void) mutex_lock(&nbtd.nbs_mtx); 455 456 if (nbtd.nbs_state == NETBIOS_STATE_ERROR) 457 error = B_TRUE; 458 else 459 error = B_FALSE; 460 461 (void) mutex_unlock(&nbtd.nbs_mtx); 462 return (error); 463 } 464 465 /* 466 * Write the service state to /var/run/smb/netbios. 467 * 468 * This is a private interface. To update the file use: 469 * smb_netbios_event(NETBIOS_EVENT_DUMP); 470 */ 471 static void 472 smb_netbios_dump(void) 473 { 474 static struct { 475 netbios_state_t state; 476 char *text; 477 } sm[] = { 478 { NETBIOS_STATE_INIT, "init" }, 479 { NETBIOS_STATE_RUNNING, "running" }, 480 { NETBIOS_STATE_CLOSING, "closing" }, 481 { NETBIOS_STATE_ERROR, "error" } 482 }; 483 484 char fname[MAXPATHLEN]; 485 FILE *fp; 486 struct passwd *pwd; 487 struct group *grp; 488 uid_t uid; 489 gid_t gid; 490 char *last_event = "none"; 491 int i; 492 493 (void) snprintf(fname, MAXPATHLEN, "%s/%s", 494 SMB_VARRUN_DIR, SMB_NETBIOS_DUMP_FILE); 495 496 if ((fp = fopen(fname, "w")) == NULL) 497 return; 498 499 pwd = getpwnam("root"); 500 grp = getgrnam("sys"); 501 uid = (pwd == NULL) ? 0 : pwd->pw_uid; 502 gid = (grp == NULL) ? 3 : grp->gr_gid; 503 504 (void) lockf(fileno(fp), F_LOCK, 0); 505 (void) fchmod(fileno(fp), 0600); 506 (void) fchown(fileno(fp), uid, gid); 507 508 if (nbtd.nbs_last_event) 509 last_event = nbtd.nbs_last_event; 510 511 for (i = 0; i < sizeof (sm) / sizeof (sm[0]); ++i) { 512 if (nbtd.nbs_state == sm[i].state) { 513 (void) fprintf(fp, 514 "State %s (event: %s, errors: %u)\n", 515 sm[i].text, last_event, nbtd.nbs_errors); 516 break; 517 } 518 } 519 520 (void) fprintf(fp, "Name Service %-7s (%u)\n", 521 nbtd.nbs_ns.s_up ? "up" : "down", nbtd.nbs_ns.s_tid); 522 (void) fprintf(fp, "Datagram Service %-7s (%u)\n", 523 nbtd.nbs_dgm.s_up ? "up" : "down", nbtd.nbs_dgm.s_tid); 524 (void) fprintf(fp, "Browser Service %-7s (%u)\n", 525 nbtd.nbs_browser.s_up ? "up" : "down", nbtd.nbs_browser.s_tid); 526 (void) fprintf(fp, "Timer Service %-7s (%u)\n", 527 nbtd.nbs_timer.s_up ? "up" : "down", nbtd.nbs_timer.s_tid); 528 529 smb_netbios_cache_dump(fp); 530 531 (void) lockf(fileno(fp), F_ULOCK, 0); 532 (void) fclose(fp); 533 } 534