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