5 #include <linux/unistd.h>
10 #include <libdevmapper.h>
15 #include <sys/mount.h>
21 #include <sysfs/libsysfs.h>
22 #include <sysfs/dlist.h>
28 #include <path_state.h>
39 #include <blacklist.h>
44 #include <devmapper.h>
46 #include <discovery.h>
50 #include <switchgroup.h>
51 #include <path_state.h>
59 #include "cli_handlers.h"
61 #define FILE_NAME_SIZE 256
64 #define LOG_MSG(a,b) \
66 condlog(a, "%s: %s", pp->dev_t, b); \
67 memset(b, 0, MAX_CHECKER_MSG_SIZE); \
72 fprintf(stderr, "%s:%s(%i) lock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \
75 fprintf(stderr, "%s:%s(%i) unlock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \
76 pthread_mutex_unlock(a)
78 #define lock(a) pthread_mutex_lock(a)
79 #define unlock(a) pthread_mutex_unlock(a)
82 pthread_cond_t exit_cond = PTHREAD_COND_INITIALIZER;
83 pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER;
92 char mapname[WWID_SIZE];
93 struct paths *allpaths;
96 static struct event_thread *
100 struct event_thread * wp;
102 wp = (struct event_thread *)MALLOC(sizeof(struct event_thread));
108 cleanup_lock (void * data)
110 unlock((pthread_mutex_t *)data);
114 set_paths_owner (struct paths * allpaths, struct multipath * mpp)
122 vector_foreach_slot (allpaths->pathvec, pp, i) {
123 if (!strncmp(mpp->wwid, pp->wwid, WWID_SIZE)) {
124 condlog(4, "%s ownership set", pp->dev_t);
131 unset_paths_owner (struct paths * allpaths, struct multipath * mpp)
136 vector_foreach_slot (allpaths->pathvec, pp, i) {
137 if (pp->mpp == mpp) {
138 condlog(4, "%s is orphaned", pp->dev_t);
145 update_multipath_table (struct multipath *mpp, vector pathvec)
150 if (dm_get_map(mpp->alias, &mpp->size, mpp->params))
153 if (disassemble_map(pathvec, mpp->params, mpp))
160 update_multipath_status (struct multipath *mpp)
165 if(dm_get_status(mpp->alias, mpp->status))
168 if (disassemble_status(mpp->status, mpp))
175 update_multipath_strings (struct multipath *mpp, vector pathvec)
179 mpp->selector = NULL;
184 mpp->features = NULL;
187 if (mpp->hwhandler) {
188 FREE(mpp->hwhandler);
189 mpp->hwhandler = NULL;
192 free_pgvec(mpp->pg, KEEP_PATHS);
195 if (update_multipath_table(mpp, pathvec))
198 if (update_multipath_status(mpp))
205 setup_multipath (struct paths * allpaths, struct multipath * mpp)
210 wwid = get_mpe_wwid(mpp->alias);
213 strncpy(mpp->wwid, wwid, WWID_SIZE);
216 strncpy(mpp->wwid, mpp->alias, WWID_SIZE);
218 condlog(4, "discovered map %s", mpp->alias);
220 if (update_multipath_strings(mpp, allpaths->pathvec))
223 set_paths_owner(allpaths, mpp);
224 mpp->mpe = find_mpe(mpp->wwid);
225 select_pgfailback(mpp);
230 * purge the multipath vector
232 if ((i = find_slot(allpaths->mpvec, (void *)mpp)) != -1)
233 vector_del_slot(allpaths->mpvec, i);
235 free_multipath(mpp, KEEP_PATHS);
236 condlog(0, "failed to setup multipath");
241 switch_pathgroup (struct multipath * mpp)
243 struct pathgroup * pgp;
247 if (!mpp || mpp->pgfailback == FAILBACK_MANUAL)
250 * Refresh path priority values
252 vector_foreach_slot (mpp->pg, pgp, i)
253 vector_foreach_slot (pgp->paths, pp, j)
254 pathinfo(pp, conf->hwtable, DI_PRIO);
256 select_path_group(mpp); /* sets mpp->nextpg */
257 pgp = VECTOR_SLOT(mpp->pg, mpp->nextpg - 1);
259 if (pgp && pgp->status != PGSTATE_ACTIVE) {
260 dm_switchgroup(mpp->alias, mpp->nextpg);
261 condlog(2, "%s: switch to path group #%i",
262 mpp->alias, mpp->nextpg);
267 update_multipath (struct paths *allpaths, char *mapname)
269 struct multipath *mpp;
270 struct pathgroup *pgp;
275 mpp = find_mp(allpaths->mpvec, mapname);
280 free_pgvec(mpp->pg, KEEP_PATHS);
283 if (setup_multipath(allpaths, mpp))
284 goto out; /* mpp freed in setup_multipath */
287 * compare checkers states with DM states
289 vector_foreach_slot (mpp->pg, pgp, i) {
290 vector_foreach_slot (pgp->paths, pp, j) {
291 if (pp->dmstate != PSTATE_FAILED)
294 if (pp->state != PATH_DOWN) {
295 condlog(2, "%s: mark as failed", pp->dev_t);
296 pp->state = PATH_DOWN;
300 * schedule the next check earlier
302 if (pp->tick > conf->checkint)
303 pp->tick = conf->checkint;
310 condlog(0, "failed to update multipath");
316 free_waiter (void * data)
318 struct event_thread * wp = (struct event_thread *)data;
321 dm_task_destroy(wp->dmt);
325 static sigset_t unblock_sigusr1(void)
330 sigaddset(&set, SIGUSR1);
331 pthread_sigmask(SIG_UNBLOCK, &set, &old);
336 * returns the reschedule delay
337 * negative means *stop*
340 waiteventloop (struct event_thread * waiter)
346 if (!waiter->event_nr)
347 waiter->event_nr = dm_geteventnr(waiter->mapname);
349 if (!(waiter->dmt = dm_task_create(DM_DEVICE_WAITEVENT)))
352 if (!dm_task_set_name(waiter->dmt, waiter->mapname))
355 if (waiter->event_nr && !dm_task_set_event_nr(waiter->dmt,
359 dm_task_no_open_count(waiter->dmt);
361 set = unblock_sigusr1();
362 dm_task_run(waiter->dmt);
363 pthread_sigmask(SIG_SETMASK, &set, NULL);
364 pthread_testcancel();
365 dm_task_destroy(waiter->dmt);
374 condlog(2, "devmap event (%i) on %s",
375 waiter->event_nr, waiter->mapname);
380 * 1) a table reload, which means our mpp structure is
381 * obsolete : refresh it through update_multipath()
382 * 2) a path failed by DM : mark as such through
384 * 3) map has gone away : stop the thread.
385 * 4) a path reinstate : nothing to do
386 * 5) a switch group : nothing to do
388 pthread_cleanup_push(cleanup_lock, waiter->allpaths->lock);
389 lock(waiter->allpaths->lock);
391 r = update_multipath(waiter->allpaths, waiter->mapname);
392 pthread_cleanup_pop(1);
395 return -1; /* stop the thread */
397 event_nr = dm_geteventnr(waiter->mapname);
399 if (waiter->event_nr == event_nr)
400 return 1; /* upon problem reschedule 1s later */
402 waiter->event_nr = event_nr;
404 return -1; /* never reach there */
408 waitevent (void * et)
411 struct event_thread *waiter;
413 mlockall(MCL_CURRENT | MCL_FUTURE);
415 waiter = (struct event_thread *)et;
416 pthread_cleanup_push(free_waiter, et);
419 r = waiteventloop(waiter);
424 pthread_testcancel();
426 pthread_testcancel();
429 pthread_cleanup_pop(1);
434 stop_waiter_thread (struct multipath * mpp, struct paths * allpaths)
436 struct event_thread * wp = (struct event_thread *)mpp->waiter;
437 pthread_t thread = wp->thread;
443 condlog(2, "%s: reap event checker", wp->mapname);
445 if ((r = pthread_cancel(thread)))
448 pthread_kill(thread, SIGUSR1);
453 start_waiter_thread (struct multipath * mpp, struct paths * allpaths)
456 struct event_thread * wp;
461 if (pthread_attr_init(&attr))
464 pthread_attr_setstacksize(&attr, 32 * 1024);
465 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
472 mpp->waiter = (void *)wp;
473 strncpy(wp->mapname, mpp->alias, WWID_SIZE);
474 wp->allpaths = allpaths;
476 if (pthread_create(&wp->thread, &attr, waitevent, wp)) {
477 condlog(0, "%s: cannot create event checker", wp->mapname);
480 condlog(2, "%s: event checker started", wp->mapname);
487 condlog(0, "failed to start waiter thread");
492 remove_map (struct multipath * mpp, struct paths * allpaths)
497 * stop the DM event waiter thread
499 if (stop_waiter_thread(mpp, allpaths)) {
500 condlog(0, "%s: error canceling waiter thread", mpp->alias);
504 free_waiter(mpp->waiter);
508 * clear references to this map
510 unset_paths_owner(allpaths, mpp);
513 * purge the multipath vector
515 i = find_slot(allpaths->mpvec, (void *)mpp);
516 vector_del_slot(allpaths->mpvec, i);
521 free_multipath(mpp, KEEP_PATHS);
526 remove_maps (struct paths * allpaths)
529 struct multipath * mpp;
531 vector_foreach_slot (allpaths->mpvec, mpp, i)
532 remove_map(mpp, allpaths);
534 vector_free(allpaths->mpvec);
535 allpaths->mpvec = NULL;
539 uev_add_map (char * devname, struct paths * allpaths)
542 char dev_t[BLK_DEV_SIZE];
544 struct multipath * mpp;
546 if (sysfs_get_dev(sysfs_path, devname, dev_t, BLK_DEV_SIZE))
549 if (sscanf(dev_t, "%d:%d", &major, &minor) != 2)
552 alias = dm_mapname(major, minor);
557 if (!dm_type(alias, DEFAULT_TARGET)) {
558 condlog(4, "%s: not a multipath map", alias);
563 mpp = find_mp(allpaths->mpvec, alias);
567 * this should not happen,
568 * we missed a remove map event (not sent ?)
570 condlog(2, "%s: already registered", alias);
571 remove_map(mpp, allpaths);
575 * now we can allocate
577 mpp = alloc_multipath();
585 if (setup_multipath(allpaths, mpp))
586 return 1; /* mpp freed in setup_multipath */
588 if (!vector_alloc_slot(allpaths->mpvec))
591 vector_set_slot(allpaths->mpvec, mpp);
592 set_paths_owner(allpaths, mpp);
594 if (start_waiter_thread(mpp, allpaths))
597 condlog(2, "add %s devmap", mpp->alias);
601 condlog(2, "add %s devmap failed", mpp->alias);
603 * purge the multipath vector
605 if ((i = find_slot(allpaths->mpvec, (void *)mpp)) != -1)
606 vector_del_slot(allpaths->mpvec, i);
608 free_multipath(mpp, KEEP_PATHS);
613 uev_remove_map (char * devname, struct paths * allpaths)
616 struct multipath * mpp;
618 if (sscanf(devname, "dm-%d", &minor) != 1)
621 mpp = find_mp_by_minor(allpaths->mpvec, minor);
624 condlog(3, "%s: devmap not registered, can't remove",
629 condlog(2, "remove %s devmap", mpp->alias);
630 remove_map(mpp, allpaths);
636 uev_add_path (char * devname, struct paths * allpaths)
640 pp = find_path_by_dev(allpaths->pathvec, devname);
643 condlog(3, "%s: already in pathvec");
646 pp = store_pathinfo(allpaths->pathvec, conf->hwtable,
647 devname, DI_SYSFS | DI_WWID);
650 condlog(0, "%s: failed to store path info", devname);
654 condlog(2, "%s: path checker registered", devname);
655 pp->mpp = find_mp_by_wwid(allpaths->mpvec, pp->wwid);
658 condlog(4, "%s: ownership set to %s",
659 pp->dev_t, pp->mpp->alias);
661 condlog(4, "%s: orphaned", pp->dev_t);
667 uev_remove_path (char * devname, struct paths * allpaths)
672 pp = find_path_by_dev(allpaths->pathvec, devname);
675 condlog(3, "%s: not in pathvec");
678 condlog(2, "remove %s path checker", devname);
679 i = find_slot(allpaths->pathvec, (void *)pp);
680 vector_del_slot(allpaths->pathvec, i);
687 show_paths (struct paths * allpaths)
694 reply = MALLOC(MAX_REPLY_LEN);
700 c += sprintf(c, "\n");
702 vector_foreach_slot(allpaths->pathvec, pp, i) {
703 c += sprintf(c, "%10s: ", pp->dev);
706 c += sprintf(c, "[orphan]\n");
710 if (MAX_REPLY_LEN - MAX_PSTATE_LEN < 0) {
715 j = pstate_snprintf(c, MAX_PSTATE_LEN, pp->state);
717 j = MAX_PSTATE_LEN - j;
723 k = pp->checkint - pp->tick;
724 c += sprintf(c, "%3i/%3i ", j, pp->checkint);
727 c += sprintf(c, "X");
731 c += sprintf(c, ".");
733 c += sprintf(c, "\n");
736 reply[MAX_REPLY_LEN - 1] = 0;
741 show_maps (struct paths * allpaths)
744 struct multipath * mpp;
748 reply = MALLOC(MAX_REPLY_LEN);
754 c += sprintf(c, "\n");
756 vector_foreach_slot(allpaths->mpvec, mpp, i) {
757 c += sprintf(c, "%20s: ", mpp->alias);
759 if (!mpp->failback_tick) {
760 c += sprintf(c, "[no scheduled failback]\n");
764 j = mpp->failback_tick;
765 k = mpp->pgfailback - mpp->failback_tick;
766 c += sprintf(c, "%3i/%3i ", j, mpp->pgfailback);
769 c += sprintf(c, "X");
773 c += sprintf(c, ".");
775 c += sprintf(c, "\n");
778 reply[MAX_REPLY_LEN - 1] = 0;
783 uxsock_trigger (char * str, void * trigger_data)
785 struct paths * allpaths;
788 allpaths = (struct paths *)trigger_data;
790 pthread_cleanup_push(cleanup_lock, allpaths->lock);
791 lock(allpaths->lock);
793 reply = parse_cmd(str, allpaths);
796 reply = STRDUP("fail\n");
798 else if (strlen(reply) == 0)
799 reply = STRDUP("ok\n");
801 pthread_cleanup_pop(1);
807 uev_trigger (struct uevent * uev, void * trigger_data)
811 struct paths * allpaths;
813 allpaths = (struct paths *)trigger_data;
814 pthread_cleanup_push(cleanup_lock, allpaths->lock);
815 lock(allpaths->lock);
817 if (strncmp(uev->devpath, "/block", 6))
820 basename(uev->devpath, devname);
823 * device map add/remove event
825 if (!strncmp(devname, "dm-", 3)) {
826 if (!strncmp(uev->action, "add", 3)) {
827 r = uev_add_map(devname, allpaths);
830 if (!strncmp(uev->action, "remove", 6)) {
831 r = uev_remove_map(devname, allpaths);
838 * path add/remove event
840 if (blacklist(conf->blist, devname))
843 if (!strncmp(uev->action, "add", 3)) {
844 r = uev_add_path(devname, allpaths);
847 if (!strncmp(uev->action, "remove", 6)) {
848 r = uev_remove_path(devname, allpaths);
854 pthread_cleanup_pop(1);
859 ueventloop (void * ap)
861 uevent_listen(&uev_trigger, ap);
867 uxlsnrloop (void * ap)
872 if (alloc_handlers())
875 add_handler(LIST+PATHS, list_paths);
876 add_handler(LIST+MAPS, list_maps);
877 add_handler(ADD+PATH, add_path);
878 add_handler(DEL+PATH, del_path);
879 add_handler(ADD+MAP, add_map);
880 add_handler(DEL+MAP, del_map);
881 add_handler(SWITCH+MAP+GROUP, switch_group);
883 uxsock_listen(&uxsock_trigger, ap);
889 exit_daemon (int status)
892 fprintf(stderr, "bad exit status. see daemon.log\n");
894 condlog(3, "unlink pidfile");
895 unlink(DEFAULT_PIDFILE);
897 condlog(2, "--------shut down-------");
900 pthread_cond_signal(&exit_cond);
907 * caller must have locked the path list before calling that function
910 get_dm_mpvec (struct paths * allpaths)
913 struct multipath * mpp;
915 if (dm_get_maps(allpaths->mpvec, "multipath"))
918 vector_foreach_slot (allpaths->mpvec, mpp, i) {
919 if (setup_multipath(allpaths, mpp))
921 mpp->minor = dm_get_minor(mpp->alias);
922 start_waiter_thread(mpp, allpaths);
929 fail_path (struct path * pp)
934 condlog(2, "checker failed path %s in map %s",
935 pp->dev_t, pp->mpp->alias);
937 dm_fail_path(pp->mpp->alias, pp->dev_t);
941 * caller must have locked the path list before calling that function
944 reinstate_path (struct path * pp)
947 if (dm_reinstate(pp->mpp->alias, pp->dev_t))
948 condlog(0, "%s: reinstate failed", pp->dev_t);
950 condlog(2, "%s: reinstated", pp->dev_t);
955 enable_group(struct path * pp)
957 struct pathgroup * pgp;
959 pgp = VECTOR_SLOT(pp->mpp->pg, pp->pgindex - 1);
961 if (pgp->status == PGSTATE_DISABLED) {
962 condlog(2, "%s: enable group #%i", pp->mpp->alias, pp->pgindex);
963 dm_enablegroup(pp->mpp->alias, pp->pgindex);
968 mpvec_garbage_collector (struct paths * allpaths)
970 struct multipath * mpp;
973 vector_foreach_slot (allpaths->mpvec, mpp, i) {
974 if (!dm_map_present(mpp->alias)) {
975 condlog(2, "%s: remove dead map", mpp->alias);
976 remove_map(mpp, allpaths);
982 defered_failback_tick (vector mpvec)
984 struct multipath * mpp;
987 vector_foreach_slot (mpvec, mpp, i) {
989 * defered failback getting sooner
991 if (mpp->pgfailback > 0 && mpp->failback_tick > 0) {
992 mpp->failback_tick--;
994 if (!mpp->failback_tick)
995 switch_pathgroup(mpp);
1001 checkerloop (void *ap)
1003 struct paths *allpaths;
1007 char checker_msg[MAX_CHECKER_MSG_SIZE];
1009 mlockall(MCL_CURRENT | MCL_FUTURE);
1011 memset(checker_msg, 0, MAX_CHECKER_MSG_SIZE);
1012 allpaths = (struct paths *)ap;
1014 condlog(2, "path checkers start up");
1017 pthread_cleanup_push(cleanup_lock, allpaths->lock);
1018 lock(allpaths->lock);
1021 vector_foreach_slot (allpaths->pathvec, pp, i) {
1027 * don't check this path yet
1034 * provision a next check soonest,
1035 * in case we exit abnormaly from here
1037 pp->tick = conf->checkint;
1040 pathinfo(pp, conf->hwtable, DI_SYSFS);
1045 condlog(0, "%s: checkfn is void", pp->dev);
1048 newstate = pp->checkfn(pp->fd, checker_msg,
1049 &pp->checker_context);
1051 if (newstate != pp->state) {
1052 pp->state = newstate;
1053 LOG_MSG(1, checker_msg);
1056 * upon state change, reset the checkint
1057 * to the shortest delay
1059 pp->checkint = conf->checkint;
1061 if (newstate == PATH_DOWN ||
1062 newstate == PATH_SHAKY) {
1064 * proactively fail path in the DM
1069 * cancel scheduled failback
1071 pp->mpp->failback_tick = 0;
1077 * reinstate this path
1082 * need to switch group ?
1084 update_multipath_strings(pp->mpp,
1088 * schedule defered failback
1090 if (pp->mpp->pgfailback > 0)
1091 pp->mpp->failback_tick =
1092 pp->mpp->pgfailback;
1094 if (pp->mpp->pgfailback == FAILBACK_IMMEDIATE)
1095 switch_pathgroup(pp->mpp);
1098 * if at least one path is up in a group, and
1099 * the group is disabled, re-enable it
1101 if (newstate == PATH_UP)
1104 else if (newstate == PATH_UP || newstate == PATH_GHOST) {
1105 LOG_MSG(4, checker_msg);
1107 * double the next check delay.
1108 * max at conf->max_checkint
1110 if (pp->checkint < (conf->max_checkint / 2))
1111 pp->checkint = 2 * pp->checkint;
1113 pp->checkint = conf->max_checkint;
1115 pp->tick = pp->checkint;
1116 condlog(4, "%s: delay next check %is",
1117 pp->dev_t, pp->tick);
1120 pp->state = newstate;
1122 defered_failback_tick(allpaths->mpvec);
1127 condlog(4, "map garbage collection");
1128 mpvec_garbage_collector(allpaths);
1132 pthread_cleanup_pop(1);
1138 static struct paths *
1141 struct paths * allpaths;
1143 allpaths = (struct paths *)MALLOC(sizeof(struct paths));
1149 (pthread_mutex_t *)MALLOC(sizeof(pthread_mutex_t));
1151 if (!allpaths->lock)
1154 allpaths->pathvec = vector_alloc();
1156 if (!allpaths->pathvec)
1159 allpaths->mpvec = vector_alloc();
1161 if (!allpaths->mpvec)
1164 pthread_mutex_init(allpaths->lock, NULL);
1169 vector_free(allpaths->pathvec);
1171 FREE(allpaths->lock);
1174 condlog(0, "failed to init paths");
1179 signal_set(int signo, void (*func) (int))
1182 struct sigaction sig;
1183 struct sigaction osig;
1185 sig.sa_handler = func;
1186 sigemptyset(&sig.sa_mask);
1189 r = sigaction(signo, &sig, &osig);
1194 return (osig.sa_handler);
1200 condlog(2, "SIGHUP received");
1203 dbg_free_final(NULL);
1216 signal_set(SIGHUP, sighup);
1217 signal_set(SIGINT, sigend);
1218 signal_set(SIGTERM, sigend);
1219 signal_set(SIGKILL, sigend);
1226 static struct sched_param sched_param = {
1230 res = sched_setscheduler (0, SCHED_RR, &sched_param);
1233 condlog(LOG_WARNING, "Could not set SCHED_RR at priority 99");
1238 set_oom_adj (int val)
1242 fp = fopen("/proc/self/oom_adj", "w");
1247 fprintf(fp, "%i", val);
1252 child (void * param)
1254 pthread_t check_thr, uevent_thr, uxlsnr_thr;
1255 pthread_attr_t attr;
1256 struct paths * allpaths;
1258 mlockall(MCL_CURRENT | MCL_FUTURE);
1263 condlog(2, "--------start up--------");
1264 condlog(2, "read " DEFAULT_CONFIGFILE);
1266 if (load_config(DEFAULT_CONFIGFILE))
1269 setlogmask(LOG_UPTO(conf->verbosity + 3));
1272 * fill the voids left in the config file
1274 if (!conf->checkint) {
1275 conf->checkint = CHECKINT;
1276 conf->max_checkint = MAX_CHECKINT;
1279 if (pidfile_create(DEFAULT_PIDFILE, getpid())) {
1288 allpaths = init_paths();
1293 if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) {
1294 condlog(0, "can not find sysfs mount point");
1299 * fetch paths and multipaths lists
1300 * no paths and/or no multipaths are valid scenarii
1301 * vectors maintenance will be driven by events
1303 path_discovery(allpaths->pathvec, conf, DI_SYSFS | DI_WWID);
1304 get_dm_mpvec(allpaths);
1309 pthread_attr_init(&attr);
1310 pthread_attr_setstacksize(&attr, 64 * 1024);
1311 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1313 pthread_create(&check_thr, &attr, checkerloop, allpaths);
1314 pthread_create(&uevent_thr, &attr, ueventloop, allpaths);
1315 pthread_create(&uxlsnr_thr, &attr, uxlsnrloop, allpaths);
1317 pthread_cond_wait(&exit_cond, &exit_mutex);
1322 lock(allpaths->lock);
1323 remove_maps(allpaths);
1324 free_pathvec(allpaths->pathvec, FREE_PATHS);
1326 pthread_cancel(check_thr);
1327 pthread_cancel(uevent_thr);
1328 pthread_cancel(uxlsnr_thr);
1331 free_handlers(handlers);
1334 unlock(allpaths->lock);
1335 pthread_mutex_destroy(allpaths->lock);
1336 FREE(allpaths->lock);
1344 dbg_free_final(NULL);
1351 main (int argc, char *argv[])
1353 extern char *optarg;
1360 if (getuid() != 0) {
1361 fprintf(stderr, "need to be root\n");
1365 /* make sure we don't lock any path */
1367 umask(umask(077) | 022);
1369 conf = alloc_config();
1374 while ((arg = getopt(argc, argv, ":dv:k::")) != EOF ) {
1378 //debug=1; /* ### comment me out ### */
1381 if (sizeof(optarg) > sizeof(char *) ||
1382 !isdigit(optarg[0]))
1385 conf->verbosity = atoi(optarg);
1405 return (child(NULL));