xref: /titanic_51/usr/src/cmd/smbsrv/smbd/smbd_main.c (revision 13faa91230bde46da937bf33010b9accc5bdeb59)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/ioccom.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <stdarg.h>
37 #include <fcntl.h>
38 #include <wait.h>
39 #include <signal.h>
40 #include <libscf.h>
41 #include <limits.h>
42 #include <priv_utils.h>
43 #include <door.h>
44 #include <errno.h>
45 #include <syslog.h>
46 #include <pthread.h>
47 #include <time.h>
48 #include <libscf.h>
49 #include <zone.h>
50 #include <tzfile.h>
51 #include <libgen.h>
52 #include <pwd.h>
53 #include <grp.h>
54 
55 #include <smbsrv/smb_door_svc.h>
56 #include <smbsrv/smb_ioctl.h>
57 #include <smbsrv/libsmb.h>
58 #include <smbsrv/libsmbns.h>
59 #include <smbsrv/libsmbrdr.h>
60 #include <smbsrv/libmlsvc.h>
61 
62 #include "smbd.h"
63 
64 #define	DRV_DEVICE_PATH	"/devices/pseudo/smbsrv@0:smbsrv"
65 #define	SMB_CACHEDIR "/var/run/smb"
66 #define	SMB_DBDIR "/var/smb"
67 
68 extern void smb_netbios_name_reconfig();
69 extern void smb_browser_config();
70 
71 static int smbd_daemonize_init(void);
72 static void smbd_daemonize_fini(int, int);
73 
74 static int smbd_kernel_bind(void);
75 static void smbd_kernel_unbind(void);
76 static int smbd_already_running(void);
77 
78 static int smbd_service_init(void);
79 static void smbd_service_fini(void);
80 
81 static int smbd_setup_options(int argc, char *argv[]);
82 static void smbd_usage(FILE *fp);
83 static void smbd_report(const char *fmt, ...);
84 
85 static void smbd_sig_handler(int sig);
86 
87 static int smbd_localtime_init(void);
88 static void *smbd_localtime_monitor(void *arg);
89 
90 extern time_t altzone;
91 
92 static pthread_t localtime_thr;
93 
94 static int smbd_refresh_init(void);
95 static void smbd_refresh_fini(void);
96 static void *smbd_refresh_monitor(void *);
97 static pthread_t refresh_thr;
98 static pthread_cond_t refresh_cond;
99 static pthread_mutex_t refresh_mutex;
100 
101 static smbd_t smbd;
102 
103 /*
104  * smbd user land daemon
105  *
106  * Use SMF error codes only on return or exit.
107  */
108 int
109 main(int argc, char *argv[])
110 {
111 	struct sigaction act;
112 	sigset_t set;
113 	uid_t uid;
114 	int pfd = -1;
115 
116 	smbd.s_pname = basename(argv[0]);
117 	openlog(smbd.s_pname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
118 
119 	if (smbd_setup_options(argc, argv) != 0)
120 		return (SMF_EXIT_ERR_FATAL);
121 
122 	if ((uid = getuid()) != smbd.s_uid) {
123 		smbd_report("user %d: %s", uid, strerror(EPERM));
124 		return (SMF_EXIT_ERR_FATAL);
125 	}
126 
127 	if (getzoneid() != GLOBAL_ZONEID) {
128 		smbd_report("non-global zones are not supported");
129 		return (SMF_EXIT_ERR_FATAL);
130 	}
131 
132 	if (is_system_labeled()) {
133 		smbd_report("Trusted Extensions not supported");
134 		return (SMF_EXIT_ERR_FATAL);
135 	}
136 
137 	if (smbd_already_running())
138 		return (SMF_EXIT_OK);
139 
140 	(void) sigfillset(&set);
141 	(void) sigdelset(&set, SIGABRT);
142 
143 	(void) sigfillset(&act.sa_mask);
144 	act.sa_handler = smbd_sig_handler;
145 	act.sa_flags = 0;
146 
147 	(void) sigaction(SIGTERM, &act, NULL);
148 	(void) sigaction(SIGHUP, &act, NULL);
149 	(void) sigaction(SIGINT, &act, NULL);
150 	(void) sigaction(SIGPIPE, &act, NULL);
151 
152 	(void) sigdelset(&set, SIGTERM);
153 	(void) sigdelset(&set, SIGHUP);
154 	(void) sigdelset(&set, SIGINT);
155 	(void) sigdelset(&set, SIGPIPE);
156 
157 	if (smbd.s_fg) {
158 		(void) sigdelset(&set, SIGTSTP);
159 		(void) sigdelset(&set, SIGTTIN);
160 		(void) sigdelset(&set, SIGTTOU);
161 
162 		if (smbd_service_init() != 0) {
163 			smbd_report("service initialization failed");
164 			exit(SMF_EXIT_ERR_FATAL);
165 		}
166 	} else {
167 		/*
168 		 * "pfd" is a pipe descriptor -- any fatal errors
169 		 * during subsequent initialization of the child
170 		 * process should be written to this pipe and the
171 		 * parent will report this error as the exit status.
172 		 */
173 		pfd = smbd_daemonize_init();
174 
175 		if (smbd_service_init() != 0) {
176 			smbd_report("daemon initialization failed");
177 			exit(SMF_EXIT_ERR_FATAL);
178 		}
179 
180 		smbd_daemonize_fini(pfd, SMF_EXIT_OK);
181 	}
182 
183 	(void) atexit(smbd_service_fini);
184 
185 	while (!smbd.s_shutdown_flag) {
186 		(void) sigsuspend(&set);
187 
188 		switch (smbd.s_sigval) {
189 		case 0:
190 			break;
191 
192 		case SIGPIPE:
193 			break;
194 
195 		case SIGHUP:
196 			/* Refresh config was triggered */
197 			if (smbd.s_fg)
198 				smbd_report("reconfiguration requested");
199 			(void) pthread_cond_signal(&refresh_cond);
200 			break;
201 
202 		default:
203 			/*
204 			 * Typically SIGINT or SIGTERM.
205 			 */
206 			smbd.s_shutdown_flag = 1;
207 			break;
208 		}
209 
210 		smbd.s_sigval = 0;
211 	}
212 
213 	smbd_service_fini();
214 	closelog();
215 	return (SMF_EXIT_OK);
216 }
217 
218 /*
219  * This function will fork off a child process,
220  * from which only the child will return.
221  *
222  * Use SMF error codes only on exit.
223  */
224 static int
225 smbd_daemonize_init(void)
226 {
227 	int status, pfds[2];
228 	sigset_t set, oset;
229 	pid_t pid;
230 	int rc;
231 
232 	/*
233 	 * Reset privileges to the minimum set required. We continue
234 	 * to run as root to create and access files in /var.
235 	 */
236 	rc = __init_daemon_priv(PU_RESETGROUPS | PU_LIMITPRIVS,
237 	    smbd.s_uid, smbd.s_gid,
238 	    PRIV_NET_MAC_AWARE, PRIV_NET_PRIVADDR, PRIV_PROC_AUDIT,
239 	    PRIV_SYS_DEVICES, PRIV_SYS_SMB, NULL);
240 
241 	if (rc != 0) {
242 		smbd_report("insufficient privileges");
243 		exit(SMF_EXIT_ERR_FATAL);
244 	}
245 
246 	/*
247 	 * Block all signals prior to the fork and leave them blocked in the
248 	 * parent so we don't get in a situation where the parent gets SIGINT
249 	 * and returns non-zero exit status and the child is actually running.
250 	 * In the child, restore the signal mask once we've done our setsid().
251 	 */
252 	(void) sigfillset(&set);
253 	(void) sigdelset(&set, SIGABRT);
254 	(void) sigprocmask(SIG_BLOCK, &set, &oset);
255 
256 	if (pipe(pfds) == -1) {
257 		smbd_report("unable to create pipe");
258 		exit(SMF_EXIT_ERR_FATAL);
259 	}
260 
261 	closelog();
262 
263 	if ((pid = fork()) == -1) {
264 		openlog(smbd.s_pname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
265 		smbd_report("unable to fork");
266 		closelog();
267 		exit(SMF_EXIT_ERR_FATAL);
268 	}
269 
270 	/*
271 	 * If we're the parent process, wait for either the child to send us
272 	 * the appropriate exit status over the pipe or for the read to fail
273 	 * (presumably with 0 for EOF if our child terminated abnormally).
274 	 * If the read fails, exit with either the child's exit status if it
275 	 * exited or with SMF_EXIT_ERR_FATAL if it died from a fatal signal.
276 	 */
277 	if (pid != 0) {
278 		(void) close(pfds[1]);
279 
280 		if (read(pfds[0], &status, sizeof (status)) == sizeof (status))
281 			_exit(status);
282 
283 		if (waitpid(pid, &status, 0) == pid && WIFEXITED(status))
284 			_exit(WEXITSTATUS(status));
285 
286 		_exit(SMF_EXIT_ERR_FATAL);
287 	}
288 
289 	openlog(smbd.s_pname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
290 	smbd.s_pid = getpid();
291 	(void) setsid();
292 	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
293 	(void) chdir("/");
294 	(void) umask(022);
295 	(void) close(pfds[0]);
296 
297 	return (pfds[1]);
298 }
299 
300 static void
301 smbd_daemonize_fini(int fd, int exit_status)
302 {
303 	/*
304 	 * Now that we're running, if a pipe fd was specified, write an exit
305 	 * status to it to indicate that our parent process can safely detach.
306 	 * Then proceed to loading the remaining non-built-in modules.
307 	 */
308 	if (fd >= 0)
309 		(void) write(fd, &exit_status, sizeof (exit_status));
310 
311 	(void) close(fd);
312 
313 	if ((fd = open("/dev/null", O_RDWR)) >= 0) {
314 		(void) fcntl(fd, F_DUP2FD, STDIN_FILENO);
315 		(void) fcntl(fd, F_DUP2FD, STDOUT_FILENO);
316 		(void) fcntl(fd, F_DUP2FD, STDERR_FILENO);
317 		(void) close(fd);
318 	}
319 
320 	__fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION,
321 	    PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, NULL);
322 }
323 
324 static int
325 smbd_service_init(void)
326 {
327 	static char *dir[] = {
328 		SMB_DBDIR,		/* smbpasswd */
329 		SMB_CACHEDIR		/* KRB credential cache */
330 	};
331 
332 	int rc;
333 	int ddns_enabled;
334 	uint32_t mode;
335 	char resource_domain[SMB_PI_MAX_DOMAIN];
336 	int i;
337 
338 	smbd.s_drv_fd = -1;
339 
340 	for (i = 0; i < (sizeof (dir)/sizeof (dir[0])); ++i) {
341 		errno = 0;
342 
343 		if ((mkdir(dir[i], 0700) < 0) && (errno != EEXIST)) {
344 			smbd_report("mkdir %s: %s", dir[i], strerror(errno));
345 			return (1);
346 		}
347 	}
348 
349 	/*
350 	 * Set KRB5CCNAME (for the SMB credential cache) in the environment.
351 	 */
352 	if (putenv("KRB5CCNAME=" SMB_CACHEDIR "/ccache") != 0) {
353 		smbd_report("unable to set KRB5CCNAME");
354 		return (1);
355 	}
356 
357 	(void) oem_language_set("english");
358 
359 	if (!nt_builtin_init()) {
360 		smbd_report("out of memory");
361 		return (1);
362 	}
363 
364 	/*
365 	 * Need to load the configuration data prior to getting the
366 	 * interface information.
367 	 */
368 	if (smb_config_load() != 0) {
369 		smbd_report("failed to load configuration data");
370 		return (1);
371 	}
372 
373 	smb_resolver_init();
374 	(void) smb_nicmon_start();
375 
376 	smbrdr_init();
377 
378 	smb_netbios_start();
379 	if (smb_netlogon_init() != 0) {
380 		smbd_report("netlogon initialization failed");
381 		return (1);
382 	}
383 
384 	smb_config_rdlock();
385 	resource_domain[0] = '\0';
386 	(void) strlcpy(resource_domain,
387 	    smb_config_getstr(SMB_CI_DOMAIN_NAME), SMB_PI_MAX_DOMAIN);
388 	(void) utf8_strupr(resource_domain);
389 	smb_config_unlock();
390 
391 	mode = smb_get_security_mode();
392 
393 	if (mode == SMB_SECMODE_DOMAIN)
394 		(void) locate_resource_pdc(resource_domain);
395 
396 	/* Get the ID map client handle */
397 	if ((rc = smb_idmap_start()) != 0) {
398 		smbd_report("no idmap handle");
399 		return (rc);
400 	}
401 
402 	if ((rc = nt_domain_init(resource_domain, mode)) != 0) {
403 		if (rc == SMB_DOMAIN_NOMACHINE_SID)
404 			smbd_report("No Security Identifier (SID) has been "
405 			    "generated for this system.\n"
406 			    "Check idmap service configuration.");
407 
408 		if (rc == SMB_DOMAIN_NODOMAIN_SID) {
409 			/* Get the domain sid from domain controller */
410 			if ((rc = lsa_query_primary_domain_info()) != 0)
411 				smbd_report("Failed to get the Security "
412 				    "Identifier (SID) of domain %s",
413 				    resource_domain);
414 			}
415 		return (rc);
416 	}
417 
418 	if ((rc = mlsvc_init()) != 0) {
419 		smbd_report("msrpc initialization failed");
420 		return (rc);
421 	}
422 
423 	if (rc = smb_mlsvc_srv_start()) {
424 		smbd_report("msrpc door initialization failed: %d", rc);
425 		return (rc);
426 	}
427 
428 	if (smb_lmshrd_srv_start() != 0) {
429 		smbd_report("share initialization failed");
430 	}
431 
432 	/* XXX following will get removed */
433 	(void) smb_doorsrv_start();
434 	if ((rc = smb_door_srv_start()) != 0)
435 		return (rc);
436 
437 	if ((rc = smbd_refresh_init()) != 0)
438 		return (rc);
439 
440 	/* Call dyndns update - Just in case its configured to refresh DNS */
441 	smb_config_rdlock();
442 	ddns_enabled = smb_config_getyorn(SMB_CI_DYNDNS_ENABLE);
443 	smb_config_unlock();
444 	if (ddns_enabled) {
445 		(void) dyndns_update();
446 	}
447 
448 	if ((rc = smbd_kernel_bind()) != 0)
449 		smbd_report("kernel bind error: %s", strerror(errno));
450 
451 	(void) smbd_localtime_init();
452 
453 	return (lmshare_start());
454 }
455 
456 /*
457  * Close the kernel service and shutdown smbd services.
458  * This function is registered with atexit(): ensure that anything
459  * called from here is safe to be called multiple times.
460  */
461 static void
462 smbd_service_fini(void)
463 {
464 	nt_builtin_fini();
465 	smbd_refresh_fini();
466 	smbd_kernel_unbind();
467 	smb_door_srv_stop();
468 	smb_doorsrv_stop();
469 	smb_lmshrd_srv_stop();
470 	lmshare_stop();
471 	smb_mlsvc_srv_stop();
472 	smb_nicmon_stop();
473 	smb_resolver_close();
474 	smb_idmap_stop();
475 }
476 
477 /*
478  * smbd_refresh_init()
479  *
480  * SMB service refresh thread initialization.  This thread waits for a
481  * refresh event and updates the daemon's view of the configuration
482  * before going back to sleep.
483  */
484 static int
485 smbd_refresh_init()
486 {
487 	pthread_attr_t tattr;
488 	pthread_condattr_t cattr;
489 	int rc;
490 
491 	(void) pthread_condattr_init(&cattr);
492 	(void) pthread_cond_init(&refresh_cond, &cattr);
493 	(void) pthread_condattr_destroy(&cattr);
494 
495 	(void) pthread_mutex_init(&refresh_mutex, NULL);
496 
497 	(void) pthread_attr_init(&tattr);
498 	(void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
499 	rc = pthread_create(&refresh_thr, &tattr, smbd_refresh_monitor, 0);
500 	(void) pthread_attr_destroy(&tattr);
501 	return (rc);
502 
503 }
504 
505 /*
506  * smbd_refresh_fini()
507  *
508  * Stop the refresh thread.
509  */
510 static void
511 smbd_refresh_fini()
512 {
513 	(void) pthread_cancel(refresh_thr);
514 
515 	(void) pthread_cond_destroy(&refresh_cond);
516 	(void) pthread_mutex_destroy(&refresh_mutex);
517 }
518 
519 /*
520  * smbd_refresh_monitor()
521  *
522  * Wait for a refresh event. When this thread wakes up, update the
523  * smbd configuration from the SMF config information then go back to
524  * wait for the next refresh.
525  */
526 /*ARGSUSED*/
527 static void *
528 smbd_refresh_monitor(void *arg)
529 {
530 	int dummy = 0;
531 
532 	(void) pthread_mutex_lock(&refresh_mutex);
533 	while (pthread_cond_wait(&refresh_cond, &refresh_mutex) == 0) {
534 		/*
535 		 * We've been woken up by a refresh event so go do
536 		 * what is necessary.
537 		 */
538 		(void) smb_config_load();
539 		smb_nic_build_info();
540 		(void) smb_netbios_name_reconfig();
541 		(void) smb_browser_config();
542 		if (ioctl(smbd.s_drv_fd, SMB_IOC_CONFIG_REFRESH, &dummy) < 0) {
543 			smbd_report("configuration update ioctl: %s",
544 			    strerror(errno));
545 		}
546 	}
547 	return (NULL);
548 }
549 
550 
551 /*
552  * If the door has already been opened by another process (non-zero pid
553  * in target), we assume that another smbd is already running.  If there
554  * is a race here, it will be caught later when smbsrv is opened because
555  * only one process is allowed to open the device at a time.
556  */
557 static int
558 smbd_already_running(void)
559 {
560 	door_info_t info;
561 	int door;
562 
563 	if ((door = open(SMBD_DOOR_NAME, O_RDONLY)) < 0)
564 		return (0);
565 
566 	if (door_info(door, &info) < 0)
567 		return (0);
568 
569 	if (info.di_target > 0) {
570 		smbd_report("already running: pid %ld\n", info.di_target);
571 		(void) close(door);
572 		return (1);
573 	}
574 
575 	(void) close(door);
576 	return (0);
577 }
578 
579 static int
580 smbd_kernel_bind(void)
581 {
582 	if (smbd.s_drv_fd != -1)
583 		(void) close(smbd.s_drv_fd);
584 
585 	if ((smbd.s_drv_fd = open(DRV_DEVICE_PATH, 0)) < 0) {
586 		smbd.s_drv_fd = -1;
587 		return (1);
588 	}
589 	return (0);
590 }
591 
592 
593 /*
594  * Initialization of the localtime thread.
595  * Returns 0 on success, an error number if thread creation fails.
596  */
597 
598 int
599 smbd_localtime_init(void)
600 {
601 	pthread_attr_t tattr;
602 	int rc;
603 
604 	(void) pthread_attr_init(&tattr);
605 	(void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
606 	rc = pthread_create(&localtime_thr, &tattr, smbd_localtime_monitor, 0);
607 	(void) pthread_attr_destroy(&tattr);
608 	return (rc);
609 }
610 
611 /*
612  * Local time thread to kernel land.
613  * Send local gmtoff to kernel module one time at startup
614  * and each time it changes (up to twice a year).
615  * Local gmtoff is checked once every 15 minutes and
616  * since some timezones are aligned on half and qtr hour boundaries,
617  * once an hour would likely suffice.
618  */
619 
620 /*ARGSUSED*/
621 static void *
622 smbd_localtime_monitor(void *arg)
623 {
624 	struct tm local_tm;
625 	time_t secs, gmtoff;
626 	time_t last_gmtoff = -1;
627 	int timeout;
628 
629 	for (;;) {
630 		gmtoff = -altzone;
631 
632 		if ((last_gmtoff != gmtoff) && (smbd.s_drv_fd != -1)) {
633 			if (ioctl(smbd.s_drv_fd, SMB_IOC_GMTOFF, &gmtoff) < 0) {
634 				smbd_report("localtime ioctl: %s",
635 				    strerror(errno));
636 			}
637 		}
638 
639 		/*
640 		 * Align the next iteration on a fifteen minute boundary.
641 		 */
642 		secs = time(0);
643 		(void) localtime_r(&secs, &local_tm);
644 		timeout = ((15 - (local_tm.tm_min % 15)) * SECSPERMIN);
645 		(void) sleep(timeout);
646 
647 		last_gmtoff = gmtoff;
648 	}
649 
650 	/*NOTREACHED*/
651 	return (NULL);
652 }
653 
654 
655 static void
656 smbd_kernel_unbind(void)
657 {
658 	if (smbd.s_drv_fd != -1) {
659 		(void) close(smbd.s_drv_fd);
660 		smbd.s_drv_fd = -1;
661 	}
662 }
663 
664 static void
665 smbd_sig_handler(int sigval)
666 {
667 	if (smbd.s_sigval == 0)
668 		smbd.s_sigval = sigval;
669 }
670 
671 /*
672  * Set up configuration options and parse the command line.
673  * This function will determine if we will run as a daemon
674  * or in the foreground.
675  *
676  * Failure to find a uid or gid results in using the default (0).
677  */
678 static int
679 smbd_setup_options(int argc, char *argv[])
680 {
681 	struct passwd *pwd;
682 	struct group *grp;
683 	int c;
684 
685 	if ((pwd = getpwnam("root")) != NULL)
686 		smbd.s_uid = pwd->pw_uid;
687 
688 	if ((grp = getgrnam("sys")) != NULL)
689 		smbd.s_gid = grp->gr_gid;
690 
691 	smbd.s_fg = smb_get_fg_flag();
692 
693 	while ((c = getopt(argc, argv, ":f")) != -1) {
694 		switch (c) {
695 		case 'f':
696 			smbd.s_fg = 1;
697 			break;
698 
699 		case ':':
700 		case '?':
701 		default:
702 			smbd_usage(stderr);
703 			return (-1);
704 		}
705 	}
706 
707 	return (0);
708 }
709 
710 static void
711 smbd_usage(FILE *fp)
712 {
713 	static char *help[] = {
714 		"-f  run program in foreground"
715 	};
716 
717 	int i;
718 
719 	(void) fprintf(fp, "Usage: %s [-f]\n", smbd.s_pname);
720 
721 	for (i = 0; i < sizeof (help)/sizeof (help[0]); ++i)
722 		(void) fprintf(fp, "    %s\n", help[i]);
723 }
724 
725 static void
726 smbd_report(const char *fmt, ...)
727 {
728 	char buf[128];
729 	va_list ap;
730 
731 	if (fmt == NULL)
732 		return;
733 
734 	va_start(ap, fmt);
735 	(void) vsnprintf(buf, 128, fmt, ap);
736 	va_end(ap);
737 
738 	(void) fprintf(stderr, "smbd: %s\n", buf);
739 }
740 
741 /*
742  * Enable libumem debugging by default on DEBUG builds.
743  */
744 #ifdef DEBUG
745 /* LINTED - external libumem symbol */
746 const char *
747 _umem_debug_init(void)
748 {
749 	return ("default,verbose"); /* $UMEM_DEBUG setting */
750 }
751 
752 /* LINTED - external libumem symbol */
753 const char *
754 _umem_logging_init(void)
755 {
756 	return ("fail,contents"); /* $UMEM_LOGGING setting */
757 }
758 #endif
759