Home | History | Annotate | Line # | Download | only in pppd
      1 /*	$NetBSD: utils.c,v 1.6 2025/01/08 19:59:39 christos Exp $	*/
      2 
      3 /*
      4  * utils.c - various utility functions used in pppd.
      5  *
      6  * Copyright (c) 1999-2024 Paul Mackerras. All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  *
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  *
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in
     17  *    the documentation and/or other materials provided with the
     18  *    distribution.
     19  *
     20  * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
     21  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
     22  * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
     23  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     24  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
     25  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     26  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __RCSID("$NetBSD: utils.c,v 1.6 2025/01/08 19:59:39 christos Exp $");
     31 
     32 #ifdef HAVE_CONFIG_H
     33 #include "config.h"
     34 #endif
     35 
     36 #include <stdarg.h>
     37 #include <stdio.h>
     38 #include <ctype.h>
     39 #include <stdlib.h>
     40 #include <string.h>
     41 #include <unistd.h>
     42 #include <signal.h>
     43 #include <errno.h>
     44 #include <fcntl.h>
     45 #include <syslog.h>
     46 #include <netdb.h>
     47 #include <time.h>
     48 #include <utmp.h>
     49 #include <pwd.h>
     50 #include <sys/param.h>
     51 #include <sys/types.h>
     52 #include <sys/wait.h>
     53 #include <sys/time.h>
     54 #include <sys/resource.h>
     55 #include <sys/stat.h>
     56 #include <sys/socket.h>
     57 #include <netinet/in.h>
     58 #ifdef SVR4
     59 #include <sys/mkdev.h>
     60 #endif
     61 
     62 #include "pppd-private.h"
     63 #include "fsm.h"
     64 #include "lcp.h"
     65 #include "pathnames.h"
     66 
     67 
     68 #if defined(SUNOS4)
     69 extern char *strerror();
     70 #endif
     71 
     72 static void logit(int, const char *, va_list);
     73 static void log_write(int, char *);
     74 static void vslp_printer(void *, char *, ...);
     75 static void format_packet(u_char *, int, printer_func, void *);
     76 
     77 struct buffer_info {
     78     char *ptr;
     79     int len;
     80 };
     81 
     82 /*
     83  * slprintf - format a message into a buffer.  Like sprintf except we
     84  * also specify the length of the output buffer, and we handle
     85  * %m (error message), %v (visible string),
     86  * %q (quoted string), %t (current time) and %I (IP address) formats.
     87  * Doesn't do floating-point formats.
     88  * Returns the number of chars put into buf.
     89  */
     90 int
     91 slprintf(char *buf, int buflen, const char *fmt, ...)
     92 {
     93     va_list args;
     94     int n;
     95 
     96     va_start(args, fmt);
     97     n = vslprintf(buf, buflen, fmt, args);
     98     va_end(args);
     99     return n;
    100 }
    101 
    102 /*
    103  * vslprintf - like slprintf, takes a va_list instead of a list of args.
    104  */
    105 #define OUTCHAR(c)	(buflen > 0? (--buflen, *buf++ = (c)): 0)
    106 
    107 int
    108 vslprintf(char *buf, int buflen, const char *fmt, va_list args)
    109 {
    110     int c, i, n;
    111     int width, prec, fillch;
    112     int base, len, neg, quoted;
    113     long long lval = 0;
    114     unsigned long long val = 0;
    115     char *str, *buf0;
    116     const char *f;
    117     unsigned char *p;
    118     char num[32];
    119     time_t t;
    120     u_int32_t ip;
    121     static char hexchars[] = "0123456789abcdef";
    122     struct buffer_info bufinfo;
    123     int termch;
    124 
    125     buf0 = buf;
    126     --buflen;
    127     while (buflen > 0) {
    128 	for (f = fmt; *f != '%' && *f != 0; ++f)
    129 	    ;
    130 	if (f > fmt) {
    131 	    len = f - fmt;
    132 	    if (len > buflen)
    133 		len = buflen;
    134 	    memcpy(buf, fmt, len);
    135 	    buf += len;
    136 	    buflen -= len;
    137 	    fmt = f;
    138 	}
    139 	if (*fmt == 0)
    140 	    break;
    141 	c = *++fmt;
    142 	width = 0;
    143 	prec = -1;
    144 	fillch = ' ';
    145 	if (c == '0') {
    146 	    fillch = '0';
    147 	    c = *++fmt;
    148 	}
    149 	if (c == '*') {
    150 	    width = va_arg(args, int);
    151 	    c = *++fmt;
    152 	} else {
    153 	    while (isdigit(c)) {
    154 		width = width * 10 + c - '0';
    155 		c = *++fmt;
    156 	    }
    157 	}
    158 	if (c == '.') {
    159 	    c = *++fmt;
    160 	    if (c == '*') {
    161 		prec = va_arg(args, int);
    162 		c = *++fmt;
    163 	    } else {
    164 		prec = 0;
    165 		while (isdigit(c)) {
    166 		    prec = prec * 10 + c - '0';
    167 		    c = *++fmt;
    168 		}
    169 	    }
    170 	}
    171 	str = 0;
    172 	base = 0;
    173 	neg = 0;
    174 	++fmt;
    175 	switch (c) {
    176 	case 'l':
    177 	    c = *fmt++;
    178 	    switch (c) {
    179 	    case 'l':
    180 		c = *fmt++;
    181 		switch (c) {
    182 		case 'd':
    183 		    lval = va_arg(args, long long);
    184 		    if (lval < 0) {
    185 			neg = 1;
    186 			val = -lval;
    187 		    } else
    188 			val = lval;
    189 		    base = 10;
    190 		    break;
    191 		case 'u':
    192 		    val = va_arg(args, unsigned long long);
    193 		    base = 10;
    194 		    break;
    195 		default:
    196 		    OUTCHAR('%');
    197 		    OUTCHAR('l');
    198 		    OUTCHAR('l');
    199 		    --fmt;		/* so %llz outputs %llz etc. */
    200 		    continue;
    201 		}
    202 		break;
    203 	    case 'd':
    204 		lval = va_arg(args, long);
    205 		if (lval < 0) {
    206 		    neg = 1;
    207 		    val = -lval;
    208 	        } else
    209 		    val = lval;
    210 		base = 10;
    211 		break;
    212 	    case 'u':
    213 		val = va_arg(args, unsigned long);
    214 		base = 10;
    215 		break;
    216 	    default:
    217 		OUTCHAR('%');
    218 		OUTCHAR('l');
    219 		--fmt;		/* so %lz outputs %lz etc. */
    220 		continue;
    221 	    }
    222 	    break;
    223 	case 'd':
    224 	    i = va_arg(args, int);
    225 	    if (i < 0) {
    226 		neg = 1;
    227 		val = -i;
    228 	    } else
    229 		val = i;
    230 	    base = 10;
    231 	    break;
    232 	case 'u':
    233 	    val = va_arg(args, unsigned int);
    234 	    base = 10;
    235 	    break;
    236 	case 'o':
    237 	    val = va_arg(args, unsigned int);
    238 	    base = 8;
    239 	    break;
    240 	case 'x':
    241 	case 'X':
    242 	    val = va_arg(args, unsigned int);
    243 	    base = 16;
    244 	    break;
    245 	case 'p':
    246 	    val = (unsigned long) va_arg(args, void *);
    247 	    base = 16;
    248 	    neg = 2;
    249 	    break;
    250 	case 's':
    251 	    str = va_arg(args, char *);
    252 	    break;
    253 	case 'c':
    254 	    num[0] = va_arg(args, int);
    255 	    num[1] = 0;
    256 	    str = num;
    257 	    break;
    258 	case 'm':
    259 	    str = strerror(errno);
    260 	    break;
    261 	case 'I':
    262 	    ip = va_arg(args, u_int32_t);
    263 	    ip = ntohl(ip);
    264 	    slprintf(num, sizeof(num), "%d.%d.%d.%d", (ip >> 24) & 0xff,
    265 		     (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
    266 	    str = num;
    267 	    break;
    268 	case 't':
    269 	    time(&t);
    270 	    str = ctime(&t);
    271 	    if ((str = ctime(&t)) == NULL)
    272 		    strlcpy(str = num, "?", sizeof(num));
    273 	    else {
    274 		    str += 4;		/* chop off the day name */
    275 		    str[15] = 0;	/* chop off year and newline */
    276 	    }
    277 	    break;
    278 	case 'v':		/* "visible" string */
    279 	case 'q':		/* quoted string */
    280 	    quoted = c == 'q';
    281 	    p = va_arg(args, unsigned char *);
    282 	    if (p == NULL)
    283 		    p = (unsigned char *)"<NULL>";
    284 	    if (fillch == '0' && prec >= 0) {
    285 		n = prec;
    286 		termch = -1;	/* matches no unsigned char value */
    287 	    } else {
    288 		n = buflen;
    289 		if (prec != -1 && n > prec)
    290 		    n = prec;
    291 		termch = 0;	/* stop on null byte */
    292 	    }
    293 	    while (n > 0 && buflen > 0) {
    294 		c = *p++;
    295 		if (c == termch)
    296 		    break;
    297 		--n;
    298 		if (!quoted && c >= 0x80) {
    299 		    OUTCHAR('M');
    300 		    OUTCHAR('-');
    301 		    c -= 0x80;
    302 		}
    303 		if (quoted && (c == '"' || c == '\\'))
    304 		    OUTCHAR('\\');
    305 		if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
    306 		    if (quoted) {
    307 			OUTCHAR('\\');
    308 			switch (c) {
    309 			case '\t':	OUTCHAR('t');	break;
    310 			case '\n':	OUTCHAR('n');	break;
    311 			case '\b':	OUTCHAR('b');	break;
    312 			case '\f':	OUTCHAR('f');	break;
    313 			default:
    314 			    OUTCHAR('x');
    315 			    OUTCHAR(hexchars[c >> 4]);
    316 			    OUTCHAR(hexchars[c & 0xf]);
    317 			}
    318 		    } else {
    319 			if (c == '\t')
    320 			    OUTCHAR(c);
    321 			else {
    322 			    OUTCHAR('^');
    323 			    OUTCHAR(c ^ 0x40);
    324 			}
    325 		    }
    326 		} else
    327 		    OUTCHAR(c);
    328 	    }
    329 	    continue;
    330 #ifndef UNIT_TEST
    331 	case 'P':		/* print PPP packet */
    332 	    bufinfo.ptr = buf;
    333 	    bufinfo.len = buflen + 1;
    334 	    p = va_arg(args, unsigned char *);
    335 	    n = va_arg(args, int);
    336 	    format_packet(p, n, vslp_printer, &bufinfo);
    337 	    buf = bufinfo.ptr;
    338 	    buflen = bufinfo.len - 1;
    339 	    continue;
    340 #endif
    341 	case 'B':
    342 	    p = va_arg(args, unsigned char *);
    343 	    for (n = prec; n > 0; --n) {
    344 		c = *p++;
    345 		if (fillch == ' ')
    346 		    OUTCHAR(' ');
    347 		OUTCHAR(hexchars[(c >> 4) & 0xf]);
    348 		OUTCHAR(hexchars[c & 0xf]);
    349 	    }
    350 	    continue;
    351 	default:
    352 	    *buf++ = '%';
    353 	    if (c != '%')
    354 		--fmt;		/* so %z outputs %z etc. */
    355 	    --buflen;
    356 	    continue;
    357 	}
    358 	if (base != 0) {
    359 	    str = num + sizeof(num);
    360 	    *--str = 0;
    361 	    while (str > num + neg) {
    362 		*--str = hexchars[val % base];
    363 		val = val / base;
    364 		if (--prec <= 0 && val == 0)
    365 		    break;
    366 	    }
    367 	    switch (neg) {
    368 	    case 1:
    369 		*--str = '-';
    370 		break;
    371 	    case 2:
    372 		*--str = 'x';
    373 		*--str = '0';
    374 		break;
    375 	    }
    376 	    len = num + sizeof(num) - 1 - str;
    377 	} else {
    378 	    for (len = 0; len < buflen && (prec == -1 || len < prec); ++len)
    379 		if (str[len] == 0)
    380 		    break;
    381 	}
    382 	if (width > 0) {
    383 	    if (width > buflen)
    384 		width = buflen;
    385 	    if ((n = width - len) > 0) {
    386 		buflen -= n;
    387 		for (; n > 0; --n)
    388 		    *buf++ = fillch;
    389 	    }
    390 	}
    391 	if (len > buflen)
    392 	    len = buflen;
    393 	memcpy(buf, str, len);
    394 	buf += len;
    395 	buflen -= len;
    396     }
    397     *buf = 0;
    398     return buf - buf0;
    399 }
    400 
    401 /*
    402  * vslp_printer - used in processing a %P format
    403  */
    404 static void
    405 vslp_printer(void *arg, char *fmt, ...)
    406 {
    407     int n;
    408     va_list pvar;
    409     struct buffer_info *bi;
    410 
    411     va_start(pvar, fmt);
    412 
    413     bi = (struct buffer_info *) arg;
    414     n = vslprintf(bi->ptr, bi->len, fmt, pvar);
    415     va_end(pvar);
    416 
    417     bi->ptr += n;
    418     bi->len -= n;
    419 }
    420 
    421 #ifdef unused
    422 /*
    423  * log_packet - format a packet and log it.
    424  */
    425 
    426 void
    427 log_packet(u_char *p, int len, char *prefix, int level)
    428 {
    429 	init_pr_log(prefix, level);
    430 	format_packet(p, len, pr_log, &level);
    431 	end_pr_log();
    432 }
    433 #endif /* unused */
    434 
    435 #ifndef UNIT_TEST
    436 /*
    437  * format_packet - make a readable representation of a packet,
    438  * calling `printer(arg, format, ...)' to output it.
    439  */
    440 static void
    441 format_packet(u_char *p, int len, printer_func printer, void *arg)
    442 {
    443     int i, n;
    444     u_short proto;
    445     struct protent *protp;
    446 
    447     if (len >= PPP_HDRLEN && p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) {
    448 	p += 2;
    449 	GETSHORT(proto, p);
    450 	len -= PPP_HDRLEN;
    451 	for (i = 0; (protp = protocols[i]) != NULL; ++i)
    452 	    if (proto == protp->protocol)
    453 		break;
    454 	if (protp != NULL) {
    455 	    printer(arg, "[%s", protp->name);
    456 	    n = (*protp->printpkt)(p, len, printer, arg);
    457 	    printer(arg, "]");
    458 	    p += n;
    459 	    len -= n;
    460 	} else {
    461 	    for (i = 0; (protp = protocols[i]) != NULL; ++i)
    462 		if (proto == (protp->protocol & ~0x8000))
    463 		    break;
    464 	    if (protp != 0 && protp->data_name != 0) {
    465 		printer(arg, "[%s data]", protp->data_name);
    466 		if (len > 8)
    467 		    printer(arg, "%.8B ...", p);
    468 		else
    469 		    printer(arg, "%.*B", len, p);
    470 		len = 0;
    471 	    } else
    472 		printer(arg, "[proto=0x%x]", proto);
    473 	}
    474     }
    475 
    476     if (len > 32)
    477 	printer(arg, "%.32B ...", p);
    478     else
    479 	printer(arg, "%.*B", len, p);
    480 }
    481 #endif  /* UNIT_TEST */
    482 
    483 /*
    484  * init_pr_log, end_pr_log - initialize and finish use of pr_log.
    485  */
    486 
    487 static char line[256];		/* line to be logged accumulated here */
    488 static char *linep;		/* current pointer within line */
    489 static int llevel;		/* level for logging */
    490 
    491 void
    492 init_pr_log(const char *prefix, int level)
    493 {
    494 	linep = line;
    495 	if (prefix != NULL) {
    496 		strlcpy(line, prefix, sizeof(line));
    497 		linep = line + strlen(line);
    498 	}
    499 	llevel = level;
    500 }
    501 
    502 void
    503 end_pr_log(void)
    504 {
    505 	if (linep != line) {
    506 		*linep = 0;
    507 		log_write(llevel, line);
    508 	}
    509 }
    510 
    511 /*
    512  * pr_log - printer routine for outputting to syslog
    513  */
    514 void
    515 pr_log(void *arg, char *fmt, ...)
    516 {
    517 	int l, n;
    518 	va_list pvar;
    519 	char *p, *eol;
    520 	char buf[256];
    521 
    522 	va_start(pvar, fmt);
    523 
    524 	n = vslprintf(buf, sizeof(buf), fmt, pvar);
    525 	va_end(pvar);
    526 
    527 	p = buf;
    528 	eol = strchr(buf, '\n');
    529 	if (linep != line) {
    530 		l = (eol == NULL)? n: eol - buf;
    531 		if (linep + l < line + sizeof(line)) {
    532 			if (l > 0) {
    533 				memcpy(linep, buf, l);
    534 				linep += l;
    535 			}
    536 			if (eol == NULL)
    537 				return;
    538 			p = eol + 1;
    539 			eol = strchr(p, '\n');
    540 		}
    541 		*linep = 0;
    542 		log_write(llevel, line);
    543 		linep = line;
    544 	}
    545 
    546 	while (eol != NULL) {
    547 		*eol = 0;
    548 		log_write(llevel, p);
    549 		p = eol + 1;
    550 		eol = strchr(p, '\n');
    551 	}
    552 
    553 	/* assumes sizeof(buf) <= sizeof(line) */
    554 	l = buf + n - p;
    555 	if (l > 0) {
    556 		memcpy(line, p, n);
    557 		linep = line + l;
    558 	}
    559 }
    560 
    561 /*
    562  * print_string - print a readable representation of a string using
    563  * printer.
    564  */
    565 void
    566 print_string(char *p, int len, printer_func printer, void *arg)
    567 {
    568     int c;
    569 
    570     printer(arg, "\"");
    571     for (; len > 0; --len) {
    572 	c = *p++;
    573 	if (' ' <= c && c <= '~') {
    574 	    if (c == '\\' || c == '"')
    575 		printer(arg, "\\");
    576 	    printer(arg, "%c", c);
    577 	} else {
    578 	    switch (c) {
    579 	    case '\n':
    580 		printer(arg, "\\n");
    581 		break;
    582 	    case '\r':
    583 		printer(arg, "\\r");
    584 		break;
    585 	    case '\t':
    586 		printer(arg, "\\t");
    587 		break;
    588 	    default:
    589 		printer(arg, "\\%.3o", (unsigned char) c);
    590 	    }
    591 	}
    592     }
    593     printer(arg, "\"");
    594 }
    595 
    596 /*
    597  * logit - does the hard work for fatal et al.
    598  */
    599 static void
    600 logit(int level, const char *fmt, va_list args)
    601 {
    602     char buf[1024];
    603 
    604     vslprintf(buf, sizeof(buf), fmt, args);
    605     log_write(level, buf);
    606 }
    607 
    608 #ifndef UNIT_TEST
    609 static void
    610 log_write(int level, char *buf)
    611 {
    612     syslog(level, "%s", buf);
    613     if (log_to_fd >= 0 && (level != LOG_DEBUG || debug)) {
    614 	int n = strlen(buf);
    615 
    616 	if (n > 0 && buf[n-1] == '\n')
    617 	    --n;
    618 	if (write(log_to_fd, buf, n) != n
    619 	    || write(log_to_fd, "\n", 1) != 1)
    620 	    log_to_fd = -1;
    621     }
    622 }
    623 #else
    624 static void
    625 log_write(int level, char *buf)
    626 {
    627     printf("<%d>: %s\n", level, buf);
    628 }
    629 #endif
    630 
    631 /*
    632  * fatal - log an error message and die horribly.
    633  */
    634 void
    635 fatal(const char *fmt, ...)
    636 {
    637     va_list pvar;
    638 
    639     va_start(pvar, fmt);
    640 
    641     logit(LOG_ERR, fmt, pvar);
    642     va_end(pvar);
    643 
    644 #ifndef UNIT_TEST
    645     die(1);			/* as promised */
    646 #else
    647     exit(-1);
    648 #endif
    649 }
    650 
    651 /*
    652  * error - log an error message.
    653  */
    654 void
    655 error(const char *fmt, ...)
    656 {
    657     va_list pvar;
    658 
    659     va_start(pvar, fmt);
    660 
    661     logit(LOG_ERR, fmt, pvar);
    662     va_end(pvar);
    663     ++error_count;
    664 }
    665 
    666 /*
    667  * warn - log a warning message.
    668  */
    669 void
    670 warn(const char *fmt, ...)
    671 {
    672     va_list pvar;
    673 
    674     va_start(pvar, fmt);
    675 
    676     logit(LOG_WARNING, fmt, pvar);
    677     va_end(pvar);
    678 }
    679 
    680 /*
    681  * notice - log a notice-level message.
    682  */
    683 void
    684 notice(const char *fmt, ...)
    685 {
    686     va_list pvar;
    687 
    688     va_start(pvar, fmt);
    689 
    690     logit(LOG_NOTICE, fmt, pvar);
    691     va_end(pvar);
    692 }
    693 
    694 /*
    695  * info - log an informational message.
    696  */
    697 void
    698 info(const char *fmt, ...)
    699 {
    700     va_list pvar;
    701 
    702     va_start(pvar, fmt);
    703 
    704     logit(LOG_INFO, fmt, pvar);
    705     va_end(pvar);
    706 }
    707 
    708 /*
    709  * dbglog - log a debug message.
    710  */
    711 void
    712 dbglog(const char *fmt, ...)
    713 {
    714     va_list pvar;
    715 
    716     va_start(pvar, fmt);
    717 
    718     logit(LOG_DEBUG, fmt, pvar);
    719     va_end(pvar);
    720 }
    721 
    722 /*
    723  * dump_packet - print out a packet in readable form if it is interesting.
    724  * Assumes len >= PPP_HDRLEN.
    725  */
    726 void
    727 dump_packet(const char *tag, unsigned char *p, int len)
    728 {
    729     int proto;
    730 
    731     if (!debug)
    732 	return;
    733 
    734     /*
    735      * don't print LCP echo request/reply packets if debug <= 1
    736      * and the link is up.
    737      */
    738     proto = (p[2] << 8) + p[3];
    739     if (debug <= 1 && unsuccess == 0 && proto == PPP_LCP
    740 	&& len >= PPP_HDRLEN + HEADERLEN) {
    741 	unsigned char *lcp = p + PPP_HDRLEN;
    742 	int l = (lcp[2] << 8) + lcp[3];
    743 
    744 	if ((lcp[0] == ECHOREQ || lcp[0] == ECHOREP)
    745 	    && l >= HEADERLEN && l <= len - PPP_HDRLEN)
    746 	    return;
    747     }
    748 
    749     dbglog("%s %P", tag, p, len);
    750 }
    751 
    752 
    753 #ifndef UNIT_TEST
    754 /*
    755  * complete_read - read a full `count' bytes from fd,
    756  * unless end-of-file or an error other than EINTR is encountered.
    757  */
    758 ssize_t
    759 complete_read(int fd, void *buf, size_t count)
    760 {
    761 	size_t done;
    762 	ssize_t nb;
    763 	char *ptr = buf;
    764 
    765 	for (done = 0; done < count; ) {
    766 		nb = read(fd, ptr, count - done);
    767 		if (nb < 0) {
    768 			if (errno == EINTR && !ppp_signaled(SIGTERM))
    769 				continue;
    770 			return -1;
    771 		}
    772 		if (nb == 0)
    773 			break;
    774 		done += nb;
    775 		ptr += nb;
    776 	}
    777 	return done;
    778 }
    779 #endif
    780 
    781 /*
    782  * mkdir_check - helper for mkdir_recursive, creates a directory
    783  * but do not error on EEXIST if and only if entry is a directory
    784  * The caller must check for errno == ENOENT if appropriate.
    785  */
    786 static int
    787 mkdir_check(const char *path)
    788 {
    789     struct stat statbuf;
    790 
    791     if (mkdir(path, 0755) >= 0)
    792 	return 0;
    793 
    794     if (errno == EEXIST) {
    795 	if (stat(path, &statbuf) < 0)
    796 	    /* got raced? */
    797 	    return -1;
    798 
    799 	if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
    800 	    return 0;
    801 
    802 	/* already exists but not a dir, treat as failure */
    803 	errno = EEXIST;
    804 	return -1;
    805     }
    806 
    807     return -1;
    808 }
    809 
    810 /*
    811  * mkdir_parent - helper for mkdir_recursive, modifies the string in place
    812  * Assumes mkdir(path) already failed, so it first creates the parent then
    813  * full path again.
    814  */
    815 static int
    816 mkdir_parent(char *path)
    817 {
    818     char *slash;
    819 
    820     slash = strrchr(path, '/');
    821     if (!slash)
    822 	return -1;
    823 
    824     *slash = 0;
    825     if (mkdir_check(path) < 0) {
    826 	if (errno != ENOENT) {
    827 	    *slash = '/';
    828 	    return -1;
    829 	}
    830 	if (mkdir_parent(path) < 0) {
    831 	    *slash = '/';
    832 	    return -1;
    833 	}
    834     }
    835     *slash = '/';
    836 
    837     return mkdir_check(path);
    838 }
    839 
    840 /*
    841  * mkdir_recursive - recursively create directory if it didn't exist
    842  */
    843 int
    844 mkdir_recursive(const char *path)
    845 {
    846     char *copy;
    847     int rc;
    848 
    849     // optimistically try on full path first to avoid allocation
    850     if (mkdir_check(path) == 0)
    851 	return 0;
    852 
    853     copy = strdup(path);
    854     if (!copy)
    855 	return -1;
    856 
    857     rc = mkdir_parent(copy);
    858     free(copy);
    859     return rc;
    860 }
    861 
    862 /* Procedures for locking the serial device using a lock file. */
    863 static char lock_file[MAXPATHLEN];
    864 
    865 /*
    866  * lock - create a lock file for the named device
    867  */
    868 int
    869 lock(char *dev)
    870 {
    871 #ifdef LOCKLIB
    872     int result;
    873 
    874     result = mklock (dev, (void *) 0);
    875     if (result == 0) {
    876 	strlcpy(lock_file, dev, sizeof(lock_file));
    877 	return 0;
    878     }
    879 
    880     if (result > 0)
    881         notice("Device %s is locked by pid %d", dev, result);
    882     else
    883 	error("Can't create lock file %s", lock_file);
    884     return -1;
    885 
    886 #else /* LOCKLIB */
    887 
    888     char lock_buffer[12];
    889     int fd, pid, n, siz;
    890 
    891 #ifdef SVR4
    892     struct stat sbuf;
    893 
    894     if (stat(dev, &sbuf) < 0) {
    895 	error("Can't get device number for %s: %m", dev);
    896 	return -1;
    897     }
    898     if ((sbuf.st_mode & S_IFMT) != S_IFCHR) {
    899 	error("Can't lock %s: not a character device", dev);
    900 	return -1;
    901     }
    902     slprintf(lock_file, sizeof(lock_file), "%s/LK.%03d.%03d.%03d",
    903 	     PPP_PATH_LOCKDIR, major(sbuf.st_dev),
    904 	     major(sbuf.st_rdev), minor(sbuf.st_rdev));
    905 #else
    906     char *p;
    907     char lockdev[MAXPATHLEN];
    908 
    909     if ((p = strstr(dev, "dev/")) != NULL) {
    910 	dev = p + 4;
    911 	strncpy(lockdev, dev, MAXPATHLEN-1);
    912 	lockdev[MAXPATHLEN-1] = 0;
    913 	while ((p = strrchr(lockdev, '/')) != NULL) {
    914 	    *p = '_';
    915 	}
    916 	dev = lockdev;
    917     } else
    918 	if ((p = strrchr(dev, '/')) != NULL)
    919 	    dev = p + 1;
    920 
    921     slprintf(lock_file, sizeof(lock_file), "%s/LCK..%s", PPP_PATH_LOCKDIR, dev);
    922 #endif
    923 
    924     while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) {
    925 	if (errno != EEXIST) {
    926 	    error("Can't create lock file %s: %m", lock_file);
    927 	    break;
    928 	}
    929 
    930 	/* Read the lock file to find out who has the device locked. */
    931 	fd = open(lock_file, O_RDONLY, 0);
    932 	if (fd < 0) {
    933 	    if (errno == ENOENT) /* This is just a timing problem. */
    934 		continue;
    935 	    error("Can't open existing lock file %s: %m", lock_file);
    936 	    break;
    937 	}
    938 #ifndef LOCK_BINARY
    939 	n = read(fd, lock_buffer, 11);
    940 #else
    941 	n = read(fd, &pid, sizeof(pid));
    942 #endif /* LOCK_BINARY */
    943 	close(fd);
    944 	fd = -1;
    945 	if (n <= 0) {
    946 	    error("Can't read pid from lock file %s", lock_file);
    947 	    break;
    948 	}
    949 
    950 	/* See if the process still exists. */
    951 #ifndef LOCK_BINARY
    952 	lock_buffer[n] = 0;
    953 	pid = atoi(lock_buffer);
    954 #endif /* LOCK_BINARY */
    955 	if (pid == getpid())
    956 	    return 1;		/* somebody else locked it for us */
    957 	if (pid == 0
    958 	    || (kill(pid, 0) == -1 && errno == ESRCH)) {
    959 	    if (unlink (lock_file) == 0) {
    960 		notice("Removed stale lock on %s (pid %d)", dev, pid);
    961 		continue;
    962 	    }
    963 	    warn("Couldn't remove stale lock on %s", dev);
    964 	} else
    965 	    notice("Device %s is locked by pid %d", dev, pid);
    966 	break;
    967     }
    968 
    969     if (fd < 0) {
    970 	lock_file[0] = 0;
    971 	return -1;
    972     }
    973 
    974     pid = getpid();
    975 #ifndef LOCK_BINARY
    976     siz = 11;
    977     slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid);
    978     n = write (fd, lock_buffer, siz);
    979 #else
    980     siz = sizeof (pid);
    981     n = write(fd, &pid, siz);
    982 #endif
    983     if (n != siz) {
    984 	error("Could not write pid to lock file when locking");
    985     }
    986     close(fd);
    987     return 0;
    988 
    989 #endif
    990 }
    991 
    992 /*
    993  * relock - called to update our lockfile when we are about to detach,
    994  * thus changing our pid (we fork, the child carries on, and the parent dies).
    995  * Note that this is called by the parent, with pid equal to the pid
    996  * of the child.  This avoids a potential race which would exist if
    997  * we had the child rewrite the lockfile (the parent might die first,
    998  * and another process could think the lock was stale if it checked
    999  * between when the parent died and the child rewrote the lockfile).
   1000  */
   1001 int
   1002 relock(int pid)
   1003 {
   1004 #ifdef LOCKLIB
   1005     /* XXX is there a way to do this? */
   1006     return -1;
   1007 #else /* LOCKLIB */
   1008 
   1009     int fd, n, siz;
   1010     char lock_buffer[12];
   1011 
   1012     if (lock_file[0] == 0)
   1013 	return -1;
   1014     fd = open(lock_file, O_WRONLY, 0);
   1015     if (fd < 0) {
   1016 	error("Couldn't reopen lock file %s: %m", lock_file);
   1017 	lock_file[0] = 0;
   1018 	return -1;
   1019     }
   1020 
   1021 #ifndef LOCK_BINARY
   1022     siz = 11;
   1023     slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid);
   1024     n = write (fd, lock_buffer, siz);
   1025 #else
   1026     siz = sizeof(pid);
   1027     n = write(fd, &pid, siz);
   1028 #endif /* LOCK_BINARY */
   1029     if (n != siz) {
   1030 	error("Could not write pid to lock file when locking");
   1031     }
   1032     close(fd);
   1033     return 0;
   1034 
   1035 #endif /* LOCKLIB */
   1036 }
   1037 
   1038 /*
   1039  * unlock - remove our lockfile
   1040  */
   1041 void
   1042 unlock(void)
   1043 {
   1044     if (lock_file[0]) {
   1045 #ifdef LOCKLIB
   1046 	(void) rmlock(lock_file, (void *) 0);
   1047 #else
   1048 	unlink(lock_file);
   1049 #endif
   1050 	lock_file[0] = 0;
   1051     }
   1052 }
   1053 
   1054