4 * Copyright (c) 1997-2009 Erez Zadok
5 * Copyright (c) 1990 Jan-Simon Pendry
6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
7 * Copyright (c) 1990 The Regents of the University of California.
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgment:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * File: am-utils/libamu/xutil.c
47 * Miscellaneous Utilities: Logging, TTY, timers, signals, RPC, memory, etc.
52 #endif /* HAVE_CONFIG_H */
57 * Logfp is the default logging device, and is initialized to stderr by
58 * default in dplog/plog below, and in
59 * amd/amfs_program.c:amfs_program_exec().
63 static char *am_progname
= "unknown"; /* "amd" */
64 static char am_hostname
[MAXHOSTNAMELEN
] = "unknown"; /* Hostname */
65 pid_t am_mypid
= -1; /* process ID */
66 serv_state amd_state
; /* amd's state */
67 int foreground
= 1; /* 1 == this is the top-level server */
68 u_int debug_flags
= D_CONTROL
; /* set regardless if compiled with debugging */
72 #endif /* HAVE_SYSLOG */
73 static u_int xlog_level
= XLOG_DEFAULT
;
74 static u_long amd_program_number
= AMQ_PROGRAM
;
77 # if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
79 static int orig_mem_bytes
;
80 # endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
81 #endif /* DEBUG_MEM */
83 /* forward definitions */
84 /* for GCC format string auditing */
85 static void real_plog(int lvl
, const char *fmt
, va_list vargs
)
86 __attribute__((__format__(__printf__
, 2, 0)));
91 * List of debug options.
93 struct opt_tab dbg_opt
[] =
95 {"all", D_ALL
}, /* All non-disruptive options */
96 {"defaults", D_DEFAULT
}, /* Default options */
97 {"test", D_TEST
}, /* Full debug - no daemon, no fork, no amq, local mtab */
98 {"amq", D_AMQ
}, /* Register for AMQ program */
99 {"daemon", D_DAEMON
}, /* Enter daemon mode */
100 {"fork", D_FORK
}, /* Fork server (hlfsd only) */
101 {"full", D_FULL
}, /* Program trace */
102 #ifdef HAVE_CLOCK_GETTIME
103 {"hrtime", D_HRTIME
}, /* Print high resolution time stamps */
104 #endif /* HAVE_CLOCK_GETTIME */
105 {"info", D_INFO
}, /* info service specific debugging (hesiod, nis, etc) */
106 {"mem", D_MEM
}, /* Trace memory allocations */
107 {"mtab", D_MTAB
}, /* Use local mtab file */
108 {"readdir", D_READDIR
}, /* Check on browsable_dirs progress */
109 {"str", D_STR
}, /* Debug string munging */
110 {"trace", D_TRACE
}, /* Protocol trace */
111 {"xdrtrace", D_XDRTRACE
}, /* Trace xdr routines */
117 * List of log options
119 struct opt_tab xlog_opt
[] =
121 {"all", XLOG_ALL
}, /* All messages */
122 {"defaults", XLOG_DEFAULT
}, /* Default messages */
124 {"debug", XLOG_DEBUG
}, /* Debug messages */
125 #endif /* DEBUG */ /* DEBUG */
126 {"error", XLOG_ERROR
}, /* Non-fatal system errors */
127 {"fatal", XLOG_FATAL
}, /* Fatal errors */
128 {"info", XLOG_INFO
}, /* Information */
129 {"map", XLOG_MAP
}, /* Map errors */
130 {"stats", XLOG_STATS
}, /* Additional statistical information */
131 {"user", XLOG_USER
}, /* Non-fatal user errors */
132 {"warn", XLOG_WARNING
}, /* Warnings */
133 {"warning", XLOG_WARNING
}, /* Warnings */
139 am_set_progname(char *pn
)
146 am_get_progname(void)
153 am_set_hostname(char *hn
)
155 xstrlcpy(am_hostname
, hn
, sizeof(am_hostname
));
160 am_get_hostname(void)
177 return (long) (foreground
? am_mypid
: getppid());
188 * Avoid malloc's which return NULL for malloc(0)
194 p
= (voidp
) malloc((unsigned) len
);
197 plog(XLOG_DEBUG
, "Allocated size %d; block %p", len
, p
);
201 plog(XLOG_ERROR
, "Retrying memory allocation");
206 plog(XLOG_FATAL
, "Out of memory");
215 /* like xmalloc, but zeros out the bytes */
219 voidp p
= xmalloc(len
);
228 xrealloc(voidp ptr
, int len
)
231 plog(XLOG_DEBUG
, "Reallocated size %d; block %p", len
, ptr
);
237 ptr
= (voidp
) realloc(ptr
, (unsigned) len
);
239 ptr
= (voidp
) xmalloc((unsigned) len
);
242 plog(XLOG_FATAL
, "Out of memory in realloc");
252 dxfree(char *file
, int line
, voidp ptr
)
255 plog(XLOG_DEBUG
, "Free in %s:%d: block %p", file
, line
, ptr
);
256 /* this is the only place that must NOT use XFREE()!!! */
258 ptr
= NULL
; /* paranoid */
262 # if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
266 struct mallinfo mi
= mallinfo();
267 u_long uordbytes
= mi
.uordblks
* 4096;
269 if (mem_bytes
!= uordbytes
) {
270 if (orig_mem_bytes
== 0)
271 mem_bytes
= orig_mem_bytes
= uordbytes
;
273 fprintf(logfp
, "%s[%ld]: ", am_get_progname(), (long) am_mypid
);
274 if (mem_bytes
< uordbytes
) {
275 fprintf(logfp
, "ALLOC: %ld bytes", uordbytes
- mem_bytes
);
277 fprintf(logfp
, "FREE: %ld bytes", mem_bytes
- uordbytes
);
279 mem_bytes
= uordbytes
;
280 fprintf(logfp
, ", making %d missing\n", mem_bytes
- orig_mem_bytes
);
285 # endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
286 #endif /* DEBUG_MEM */
290 * Take a log format string and expand occurrences of %m
291 * with the current error code taken from errno. Make sure
292 * 'e' never gets longer than maxlen characters.
295 expand_error(const char *f
, char *e
, size_t maxlen
)
302 for (p
= f
, q
= e
; (*q
= *p
) && (size_t) len
< maxlen
; len
++, q
++, p
++) {
303 if (p
[0] == '%' && p
[1] == 'm') {
304 xstrlcpy(q
, strerror(error
), maxlen
);
305 len
+= strlen(q
) - 1;
310 e
[maxlen
-1] = '\0'; /* null terminate, to be sure */
316 * Output the time of day and hostname to the logfile
319 show_time_host_and_name(int lvl
)
321 static time_t last_t
= 0;
322 static char *last_ctime
= NULL
;
324 #if defined(HAVE_CLOCK_GETTIME) && defined(DEBUG)
326 #endif /* defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) */
327 char nsecs
[11]; /* '.' + 9 digits + '\0' */
332 #if defined(HAVE_CLOCK_GETTIME) && defined(DEBUG)
334 * Some systems (AIX 4.3) seem to implement clock_gettime() as stub
337 if (clock_gettime(CLOCK_REALTIME
, &ts
) == 0) {
339 if (amuDebug(D_HRTIME
))
340 xsnprintf(nsecs
, sizeof(nsecs
), ".%09ld", ts
.tv_nsec
);
343 #endif /* defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) */
347 last_ctime
= ctime(&t
);
380 fprintf(logfp
, "%15.15s%s %s %s[%ld]/%s ",
381 last_ctime
+ 4, nsecs
, am_get_hostname(),
390 * Switch on/off debug options
393 debug_option(char *opt
)
395 u_int dl
= debug_flags
;
396 static int initialized_debug_flags
= 0;
397 int rc
= cmdoption(opt
, dbg_opt
, &dl
);
399 if (rc
) /* if got any error, don't update debug flags */
403 * If we already initialized the debugging flags once (via amd.conf), then
404 * don't allow "immutable" flags to be changed again (via amq -D), because
405 * they could mess Amd's state and only make sense to be set once when Amd
408 if (initialized_debug_flags
&&
410 (dl
& D_IMMUTABLE
) != (debug_flags
& D_IMMUTABLE
)) {
411 plog(XLOG_ERROR
, "cannot change immutable debug flags");
412 /* undo any attempted change to an immutable flag */
413 dl
= (dl
& ~D_IMMUTABLE
) | (debug_flags
& D_IMMUTABLE
);
415 initialized_debug_flags
= 1;
423 dplog(const char *fmt
, ...)
428 logfp
= stderr
; /* initialize before possible first use */
431 real_plog(XLOG_DEBUG
, fmt
, ap
);
438 plog(int lvl
, const char *fmt
, ...)
443 logfp
= stderr
; /* initialize before possible first use */
446 real_plog(lvl
, fmt
, ap
);
452 real_plog(int lvl
, const char *fmt
, va_list vargs
)
457 static char last_msg
[1024];
458 static int last_count
= 0, last_lvl
= 0;
460 if (!(xlog_level
& lvl
))
464 # if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
466 # endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
467 #endif /* DEBUG_MEM */
470 * Note: xvsnprintf() may call plog() if a truncation happened, but the
471 * latter has some code to break out of an infinite loop. See comment in
474 xvsnprintf(ptr
, 1023, expand_error(fmt
, efmt
, 1024), vargs
);
477 if (*(ptr
-1) == '\n')
482 switch (lvl
) { /* from mike <mcooper@usc.edu> */
511 syslog(lvl
, "%s", msg
);
514 #endif /* HAVE_SYSLOG */
520 * mimic syslog behavior: only write repeated strings if they differ
522 switch (last_count
) {
523 case 0: /* never printed at all */
525 if (strlcpy(last_msg
, msg
, sizeof(last_msg
)) >= sizeof(last_msg
)) /* don't use xstrlcpy here (recursive!) */
526 fprintf(stderr
, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg
, msg
);
528 show_time_host_and_name(lvl
); /* mimic syslog header */
529 fwrite(msg
, ptr
- msg
, 1, logfp
);
533 case 1: /* item printed once, if same, don't repeat */
534 if (STREQ(last_msg
, msg
)) {
536 } else { /* last msg printed once, new one differs */
537 /* last_count remains at 1 */
538 if (strlcpy(last_msg
, msg
, sizeof(last_msg
)) >= sizeof(last_msg
)) /* don't use xstrlcpy here (recursive!) */
539 fprintf(stderr
, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg
, msg
);
541 show_time_host_and_name(lvl
); /* mimic syslog header */
542 fwrite(msg
, ptr
- msg
, 1, logfp
);
549 * Don't allow repetitions longer than 100, so you can see when something
552 show_time_host_and_name(last_lvl
);
553 xsnprintf(last_msg
, sizeof(last_msg
),
554 "last message repeated %d times\n", last_count
);
555 fwrite(last_msg
, strlen(last_msg
), 1, logfp
);
557 last_count
= 0; /* start from scratch */
560 default: /* item repeated multiple times */
561 if (STREQ(last_msg
, msg
)) {
563 } else { /* last msg repeated+skipped, new one differs */
564 show_time_host_and_name(last_lvl
);
565 xsnprintf(last_msg
, sizeof(last_msg
),
566 "last message repeated %d times\n", last_count
);
567 fwrite(last_msg
, strlen(last_msg
), 1, logfp
);
568 if (strlcpy(last_msg
, msg
, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */
569 fprintf(stderr
, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg
, msg
);
572 show_time_host_and_name(lvl
); /* mimic syslog header */
573 fwrite(msg
, ptr
- msg
, 1, logfp
);
583 * Display current debug options
586 show_opts(int ch
, struct opt_tab
*opts
)
591 fprintf(stderr
, "\t[-%c {no}", ch
);
592 for (i
= 0; opts
[i
].opt
; i
++) {
593 fprintf(stderr
, "%c%s", s
, opts
[i
].opt
);
596 fputs("}]\n", stderr
);
601 cmdoption(char *s
, struct opt_tab
*optb
, u_int
*flags
)
609 struct opt_tab
*dp
, *dpn
= NULL
;
616 /* check for "no" prefix to options */
617 if (s
[0] == 'n' && s
[1] == 'o') {
626 * Scan the array of debug options to find the
627 * corresponding flag value. If it is found
628 * then set (or clear) the flag (depending on
629 * whether the option was prefixed with "no").
631 for (dp
= optb
; dp
->opt
; dp
++) {
632 if (STREQ(opt
, dp
->opt
))
634 if (opt
!= s
&& !dpn
&& STREQ(s
, dp
->opt
))
638 if (dp
->opt
|| dpn
) {
649 * This will log to stderr when parsing the command line
650 * since any -l option will not yet have taken effect.
652 plog(XLOG_ERROR
, "option \"%s\" not recognized", s
);
668 * Switch on/off logging options
671 switch_option(char *opt
)
673 u_int xl
= xlog_level
;
674 int rc
= cmdoption(opt
, xlog_opt
, &xl
);
676 if (rc
) /* if got any error, don't update flags */
680 * Don't allow "mandatory" flags to be turned off, because
681 * we must always be able to report on flag re/setting errors.
683 if ((xl
& XLOG_MANDATORY
) != XLOG_MANDATORY
) {
684 plog(XLOG_ERROR
, "cannot turn off mandatory logging options");
685 xl
|= XLOG_MANDATORY
;
687 if (xlog_level
!= xl
)
688 xlog_level
= xl
; /* set new flags */
695 * get syslog facility to use.
696 * logfile can be "syslog", "syslog:daemon", "syslog:local7", etc.
699 get_syslog_facility(const char *logfile
)
703 /* parse facility string */
704 facstr
= strchr(logfile
, ':');
705 if (!facstr
) /* log file was "syslog" */
708 if (!facstr
|| facstr
[0] == '\0') { /* log file was "syslog:" */
709 plog(XLOG_WARNING
, "null syslog facility, using LOG_DAEMON");
714 if (STREQ(facstr
, "kern"))
716 #endif /* not LOG_KERN */
718 if (STREQ(facstr
, "user"))
720 #endif /* not LOG_USER */
722 if (STREQ(facstr
, "mail"))
724 #endif /* not LOG_MAIL */
726 if (STREQ(facstr
, "daemon"))
730 if (STREQ(facstr
, "auth"))
732 #endif /* not LOG_AUTH */
734 if (STREQ(facstr
, "syslog"))
736 #endif /* not LOG_SYSLOG */
738 if (STREQ(facstr
, "lpr"))
740 #endif /* not LOG_LPR */
742 if (STREQ(facstr
, "news"))
744 #endif /* not LOG_NEWS */
746 if (STREQ(facstr
, "uucp"))
748 #endif /* not LOG_UUCP */
750 if (STREQ(facstr
, "cron"))
752 #endif /* not LOG_CRON */
754 if (STREQ(facstr
, "local0"))
756 #endif /* not LOG_LOCAL0 */
758 if (STREQ(facstr
, "local1"))
760 #endif /* not LOG_LOCAL1 */
762 if (STREQ(facstr
, "local2"))
764 #endif /* not LOG_LOCAL2 */
766 if (STREQ(facstr
, "local3"))
768 #endif /* not LOG_LOCAL3 */
770 if (STREQ(facstr
, "local4"))
772 #endif /* not LOG_LOCAL4 */
774 if (STREQ(facstr
, "local5"))
776 #endif /* not LOG_LOCAL5 */
778 if (STREQ(facstr
, "local6"))
780 #endif /* not LOG_LOCAL6 */
782 if (STREQ(facstr
, "local7"))
784 #endif /* not LOG_LOCAL7 */
786 /* didn't match anything else */
787 plog(XLOG_WARNING
, "unknown syslog facility \"%s\", using LOG_DAEMON", facstr
);
790 #endif /* not LOG_DAEMON */
794 * Change current logfile
797 switch_to_logfile(char *logfile
, int old_umask
, int truncate_log
)
799 FILE *new_logfp
= stderr
;
804 #endif /* HAVE_SYSLOG */
806 if (STREQ(logfile
, "/dev/stderr"))
808 else if (NSTREQ(logfile
, "syslog", strlen("syslog"))) {
813 openlog(am_get_progname(),
817 # endif /* LOG_NOWAIT */
819 , get_syslog_facility(logfile
)
820 # endif /* LOG_DAEMON */
822 #else /* not HAVE_SYSLOG */
823 plog(XLOG_WARNING
, "syslog option not supported, logging unchanged");
824 #endif /* not HAVE_SYSLOG */
826 } else { /* regular log file */
827 (void) umask(old_umask
);
829 truncate(logfile
, 0);
830 new_logfp
= fopen(logfile
, "a");
836 * If we couldn't open a new file, then continue using the old.
838 if (!new_logfp
&& logfile
) {
839 plog(XLOG_USER
, "%s: Can't open logfile: %m", logfile
);
844 * Close the previous file
846 if (logfp
&& logfp
!= stderr
)
847 (void) fclose(logfp
);
851 plog(XLOG_INFO
, "switched to logfile \"%s\"", logfile
);
853 plog(XLOG_INFO
, "no logfile defined; using stderr");
863 if (amuDebug(D_AMQ
)) {
864 /* find which instance of amd to unregister */
865 u_long amd_prognum
= get_amd_program_number();
867 if (pmap_unset(amd_prognum
, AMQ_VERSION
) != 1)
868 dlog("failed to de-register Amd program %lu, version %lu",
869 amd_prognum
, AMQ_VERSION
);
878 if (amd_state
!= Start
) {
879 if (amd_state
!= Done
)
885 #ifdef MOUNT_TABLE_ON_FILE
887 * Call unlock_mntlist to free any important resources such as an on-disk
888 * lock file (/etc/mtab~).
891 #endif /* MOUNT_TABLE_ON_FILE */
894 plog(XLOG_INFO
, "Finishing with status %d", rc
);
896 dlog("background process exiting with status %d", rc
);
903 /* return the rpc program number under which amd was used */
905 get_amd_program_number(void)
907 return amd_program_number
;
911 /* set the rpc program number used for amd */
913 set_amd_program_number(u_long program
)
915 amd_program_number
= program
;
920 * Release the controlling tty of the process pid.
922 * Algorithm: try these in order, if available, until one of them
923 * succeeds: setsid(), ioctl(fd, TIOCNOTTY, 0).
924 * Do not use setpgid(): on some OSs it may release the controlling tty,
925 * even if the man page does not mention it, but on other OSs it does not.
926 * Also avoid setpgrp(): it works on some systems, and on others it is
927 * identical to setpgid().
930 amu_release_controlling_tty(void)
935 * In daemon mode, leaving open file descriptors to terminals or pipes
936 * can be a really bad idea.
937 * Case in point: the redhat startup script calls us through their 'initlog'
938 * program, which exits as soon as the original amd process exits. If,
939 * at some point, a misbehaved library function decides to print something
940 * to the screen, we get a SIGPIPE and die.
941 * And guess what: NIS glibc functions will attempt to print to stderr
942 * "YPBINDPROC_DOMAIN: Domain not bound" if ypbind is running but can't find
945 * So we close all of our "terminal" filedescriptors, i.e. 0, 1 and 2, then
946 * reopen them as /dev/null.
948 * XXX We should also probably set the SIGPIPE handler to SIG_IGN.
950 fd
= open("/dev/null", O_RDWR
);
952 plog(XLOG_WARNING
, "Could not open /dev/null for rw: %m");
954 fflush(stdin
); close(0); dup2(fd
, 0);
955 fflush(stdout
); close(1); dup2(fd
, 1);
956 fflush(stderr
); close(2); dup2(fd
, 2);
961 /* XXX: one day maybe use vhangup(2) */
963 plog(XLOG_WARNING
, "Could not release controlling tty using setsid(): %m");
965 plog(XLOG_INFO
, "released controlling tty using setsid()");
968 #endif /* HAVE_SETSID */
971 fd
= open("/dev/tty", O_RDWR
);
973 /* not an error if already no controlling tty */
975 plog(XLOG_WARNING
, "Could not open controlling tty: %m");
977 if (ioctl(fd
, TIOCNOTTY
, 0) < 0 && errno
!= ENOTTY
)
978 plog(XLOG_WARNING
, "Could not disassociate tty (TIOCNOTTY): %m");
980 plog(XLOG_INFO
, "released controlling tty using ioctl(TIOCNOTTY)");
984 #endif /* not TIOCNOTTY */
986 plog(XLOG_ERROR
, "unable to release controlling tty");
990 /* setup a single signal handler */
992 setup_sighandler(int signum
, void (*handler
)(int))
994 #ifdef HAVE_SIGACTION
996 memset(&sa
, 0, sizeof(sa
));
997 sa
.sa_flags
= 0; /* unnecessary */
998 sa
.sa_handler
= handler
;
999 sigemptyset(&(sa
.sa_mask
)); /* probably unnecessary too */
1000 sigaddset(&(sa
.sa_mask
), signum
);
1001 sigaction(signum
, &sa
, NULL
);
1002 #else /* not HAVE_SIGACTION */
1003 (void) signal(signum
, handler
);
1004 #endif /* not HAVE_SIGACTION */
1009 * Return current time in seconds. If passed a non-null argyument, then
1010 * fill it in with the current time in seconds and microseconds (useful
1011 * for mtime updates).
1014 clocktime(nfstime
*nt
)
1016 static struct timeval now
; /* keep last time, as default */
1018 if (gettimeofday(&now
, NULL
) < 0) {
1019 plog(XLOG_ERROR
, "clocktime: gettimeofday: %m");
1020 /* hack: force time to have incremented by at least 1 second */
1023 /* copy seconds and microseconds. may demote a long to an int */
1025 nt
->nt_seconds
= (u_int
) now
.tv_sec
;
1026 nt
->nt_useconds
= (u_int
) now
.tv_usec
;
1028 return (time_t) now
.tv_sec
;
1033 * Make all the directories in the path.
1036 mkdirs(char *path
, int mode
)
1039 * take a copy in case path is in readonly store
1041 char *p2
= strdup(path
);
1044 int error_so_far
= 0;
1047 * Skip through the string make the directories.
1048 * Mostly ignore errors - the result is tested at the end.
1050 * This assumes we are root so that we can do mkdir in a
1051 * mode 555 directory...
1053 while ((sp
= strchr(sp
+ 1, '/'))) {
1055 if (mkdir(p2
, mode
) < 0) {
1056 error_so_far
= errno
;
1058 dlog("mkdir(%s)", p2
);
1063 if (mkdir(p2
, mode
) < 0) {
1064 error_so_far
= errno
;
1066 dlog("mkdir(%s)", p2
);
1071 return stat(path
, &stb
) == 0 &&
1072 (stb
.st_mode
& S_IFMT
) == S_IFDIR
? 0 : error_so_far
;
1077 * Remove as many directories in the path as possible.
1078 * Give up if the directory doesn't appear to have
1079 * been created by Amd (not mode dr-x) or an rmdir
1080 * fails for any reason.
1085 char *xdp
= strdup(dir
);
1091 * Try to find out whether this was
1092 * created by amd. Do this by checking
1093 * for owner write permission.
1095 if (stat(xdp
, &stb
) == 0 && (stb
.st_mode
& 0200) == 0) {
1096 if (rmdir(xdp
) < 0) {
1097 if (errno
!= ENOTEMPTY
&&
1102 plog(XLOG_ERROR
, "rmdir(%s): %m", xdp
);
1105 dlog("rmdir(%s)", xdp
);
1111 dp
= strrchr(xdp
, '/');
1114 } while (dp
&& dp
> xdp
);