xref: /netbsd-src/external/bsd/ppp/dist/pppd/utils.c (revision f12839c5f795a8def46f685de6698463dbd213a9)
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