xref: /netbsd-src/external/bsd/ppp/dist/pppd/utils.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: utils.c,v 1.4 2014/10/25 21:11:37 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.4 2014/10/25 21:11:37 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 (p == NULL)
272 		    p = (unsigned char *)"<NULL>";
273 	    if (fillch == '0' && prec >= 0) {
274 		n = prec;
275 	    } else {
276 		n = strlen((char *)p);
277 		if (prec >= 0 && n > prec)
278 		    n = prec;
279 	    }
280 	    while (n > 0 && buflen > 0) {
281 		c = *p++;
282 		--n;
283 		if (!quoted && c >= 0x80) {
284 		    OUTCHAR('M');
285 		    OUTCHAR('-');
286 		    c -= 0x80;
287 		}
288 		if (quoted && (c == '"' || c == '\\'))
289 		    OUTCHAR('\\');
290 		if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
291 		    if (quoted) {
292 			OUTCHAR('\\');
293 			switch (c) {
294 			case '\t':	OUTCHAR('t');	break;
295 			case '\n':	OUTCHAR('n');	break;
296 			case '\b':	OUTCHAR('b');	break;
297 			case '\f':	OUTCHAR('f');	break;
298 			default:
299 			    OUTCHAR('x');
300 			    OUTCHAR(hexchars[c >> 4]);
301 			    OUTCHAR(hexchars[c & 0xf]);
302 			}
303 		    } else {
304 			if (c == '\t')
305 			    OUTCHAR(c);
306 			else {
307 			    OUTCHAR('^');
308 			    OUTCHAR(c ^ 0x40);
309 			}
310 		    }
311 		} else
312 		    OUTCHAR(c);
313 	    }
314 	    continue;
315 	case 'P':		/* print PPP packet */
316 	    bufinfo.ptr = buf;
317 	    bufinfo.len = buflen + 1;
318 	    p = va_arg(args, unsigned char *);
319 	    n = va_arg(args, int);
320 	    format_packet(p, n, vslp_printer, &bufinfo);
321 	    buf = bufinfo.ptr;
322 	    buflen = bufinfo.len - 1;
323 	    continue;
324 	case 'B':
325 	    p = va_arg(args, unsigned char *);
326 	    for (n = prec; n > 0; --n) {
327 		c = *p++;
328 		if (fillch == ' ')
329 		    OUTCHAR(' ');
330 		OUTCHAR(hexchars[(c >> 4) & 0xf]);
331 		OUTCHAR(hexchars[c & 0xf]);
332 	    }
333 	    continue;
334 	default:
335 	    *buf++ = '%';
336 	    if (c != '%')
337 		--fmt;		/* so %z outputs %z etc. */
338 	    --buflen;
339 	    continue;
340 	}
341 	if (base != 0) {
342 	    str = num + sizeof(num);
343 	    *--str = 0;
344 	    while (str > num + neg) {
345 		*--str = hexchars[val % base];
346 		val = val / base;
347 		if (--prec <= 0 && val == 0)
348 		    break;
349 	    }
350 	    switch (neg) {
351 	    case 1:
352 		*--str = '-';
353 		break;
354 	    case 2:
355 		*--str = 'x';
356 		*--str = '0';
357 		break;
358 	    }
359 	    len = num + sizeof(num) - 1 - str;
360 	} else {
361 	    len = strlen(str);
362 	    if (prec >= 0 && len > prec)
363 		len = prec;
364 	}
365 	if (width > 0) {
366 	    if (width > buflen)
367 		width = buflen;
368 	    if ((n = width - len) > 0) {
369 		buflen -= n;
370 		for (; n > 0; --n)
371 		    *buf++ = fillch;
372 	    }
373 	}
374 	if (len > buflen)
375 	    len = buflen;
376 	memcpy(buf, str, len);
377 	buf += len;
378 	buflen -= len;
379     }
380     *buf = 0;
381     return buf - buf0;
382 }
383 
384 /*
385  * vslp_printer - used in processing a %P format
386  */
387 static void
388 vslp_printer __V((void *arg, char *fmt, ...))
389 {
390     int n;
391     va_list pvar;
392     struct buffer_info *bi;
393 
394 #if defined(__STDC__)
395     va_start(pvar, fmt);
396 #else
397     void *arg;
398     char *fmt;
399     va_start(pvar);
400     arg = va_arg(pvar, void *);
401     fmt = va_arg(pvar, char *);
402 #endif
403 
404     bi = (struct buffer_info *) arg;
405     n = vslprintf(bi->ptr, bi->len, fmt, pvar);
406     va_end(pvar);
407 
408     bi->ptr += n;
409     bi->len -= n;
410 }
411 
412 #ifdef unused
413 /*
414  * log_packet - format a packet and log it.
415  */
416 
417 void
418 log_packet(p, len, prefix, level)
419     u_char *p;
420     int len;
421     char *prefix;
422     int level;
423 {
424 	init_pr_log(prefix, level);
425 	format_packet(p, len, pr_log, &level);
426 	end_pr_log();
427 }
428 #endif /* unused */
429 
430 /*
431  * format_packet - make a readable representation of a packet,
432  * calling `printer(arg, format, ...)' to output it.
433  */
434 static void
435 format_packet(p, len, printer, arg)
436     u_char *p;
437     int len;
438     printer_func printer;
439     void *arg;
440 {
441     int i, n;
442     u_short proto;
443     struct protent *protp;
444 
445     if (len >= PPP_HDRLEN && p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) {
446 	p += 2;
447 	GETSHORT(proto, p);
448 	len -= PPP_HDRLEN;
449 	for (i = 0; (protp = protocols[i]) != NULL; ++i)
450 	    if (proto == protp->protocol)
451 		break;
452 	if (protp != NULL) {
453 	    printer(arg, "[%s", protp->name);
454 	    n = (*protp->printpkt)(p, len, printer, arg);
455 	    printer(arg, "]");
456 	    p += n;
457 	    len -= n;
458 	} else {
459 	    for (i = 0; (protp = protocols[i]) != NULL; ++i)
460 		if (proto == (protp->protocol & ~0x8000))
461 		    break;
462 	    if (protp != 0 && protp->data_name != 0) {
463 		printer(arg, "[%s data]", protp->data_name);
464 		if (len > 8)
465 		    printer(arg, "%.8B ...", p);
466 		else
467 		    printer(arg, "%.*B", len, p);
468 		len = 0;
469 	    } else
470 		printer(arg, "[proto=0x%x]", proto);
471 	}
472     }
473 
474     if (len > 32)
475 	printer(arg, "%.32B ...", p);
476     else
477 	printer(arg, "%.*B", len, p);
478 }
479 
480 /*
481  * init_pr_log, end_pr_log - initialize and finish use of pr_log.
482  */
483 
484 static char line[256];		/* line to be logged accumulated here */
485 static char *linep;		/* current pointer within line */
486 static int llevel;		/* level for logging */
487 
488 void
489 init_pr_log(prefix, level)
490      const char *prefix;
491      int level;
492 {
493 	linep = line;
494 	if (prefix != NULL) {
495 		strlcpy(line, prefix, sizeof(line));
496 		linep = line + strlen(line);
497 	}
498 	llevel = level;
499 }
500 
501 void
502 end_pr_log()
503 {
504 	if (linep != line) {
505 		*linep = 0;
506 		log_write(llevel, line);
507 	}
508 }
509 
510 /*
511  * pr_log - printer routine for outputting to syslog
512  */
513 void
514 pr_log __V((void *arg, char *fmt, ...))
515 {
516 	int l, n;
517 	va_list pvar;
518 	char *p, *eol;
519 	char buf[256];
520 
521 #if defined(__STDC__)
522 	va_start(pvar, fmt);
523 #else
524 	void *arg;
525 	char *fmt;
526 	va_start(pvar);
527 	arg = va_arg(pvar, void *);
528 	fmt = va_arg(pvar, char *);
529 #endif
530 
531 	n = vslprintf(buf, sizeof(buf), fmt, pvar);
532 	va_end(pvar);
533 
534 	p = buf;
535 	eol = strchr(buf, '\n');
536 	if (linep != line) {
537 		l = (eol == NULL)? n: eol - buf;
538 		if (linep + l < line + sizeof(line)) {
539 			if (l > 0) {
540 				memcpy(linep, buf, l);
541 				linep += l;
542 			}
543 			if (eol == NULL)
544 				return;
545 			p = eol + 1;
546 			eol = strchr(p, '\n');
547 		}
548 		*linep = 0;
549 		log_write(llevel, line);
550 		linep = line;
551 	}
552 
553 	while (eol != NULL) {
554 		*eol = 0;
555 		log_write(llevel, p);
556 		p = eol + 1;
557 		eol = strchr(p, '\n');
558 	}
559 
560 	/* assumes sizeof(buf) <= sizeof(line) */
561 	l = buf + n - p;
562 	if (l > 0) {
563 		memcpy(line, p, n);
564 		linep = line + l;
565 	}
566 }
567 
568 /*
569  * print_string - print a readable representation of a string using
570  * printer.
571  */
572 void
573 print_string(p, len, printer, arg)
574     char *p;
575     int len;
576     printer_func printer;
577     void *arg;
578 {
579     int c;
580 
581     printer(arg, "\"");
582     for (; len > 0; --len) {
583 	c = *p++;
584 	if (' ' <= c && c <= '~') {
585 	    if (c == '\\' || c == '"')
586 		printer(arg, "\\");
587 	    printer(arg, "%c", c);
588 	} else {
589 	    switch (c) {
590 	    case '\n':
591 		printer(arg, "\\n");
592 		break;
593 	    case '\r':
594 		printer(arg, "\\r");
595 		break;
596 	    case '\t':
597 		printer(arg, "\\t");
598 		break;
599 	    default:
600 		printer(arg, "\\%.3o", c);
601 	    }
602 	}
603     }
604     printer(arg, "\"");
605 }
606 
607 /*
608  * logit - does the hard work for fatal et al.
609  */
610 static void
611 logit(level, fmt, args)
612     int level;
613     char *fmt;
614     va_list args;
615 {
616     char buf[1024];
617 
618     vslprintf(buf, sizeof(buf), fmt, args);
619     log_write(level, buf);
620 }
621 
622 static void
623 log_write(level, buf)
624     int level;
625     char *buf;
626 {
627     syslog(level, "%s", buf);
628     if (log_to_fd >= 0 && (level != LOG_DEBUG || debug)) {
629 	int n = strlen(buf);
630 
631 	if (n > 0 && buf[n-1] == '\n')
632 	    --n;
633 	if (write(log_to_fd, buf, n) != n
634 	    || write(log_to_fd, "\n", 1) != 1)
635 	    log_to_fd = -1;
636     }
637 }
638 
639 /*
640  * fatal - log an error message and die horribly.
641  */
642 void
643 fatal __V((char *fmt, ...))
644 {
645     va_list pvar;
646 
647 #if defined(__STDC__)
648     va_start(pvar, fmt);
649 #else
650     char *fmt;
651     va_start(pvar);
652     fmt = va_arg(pvar, char *);
653 #endif
654 
655     logit(LOG_ERR, fmt, pvar);
656     va_end(pvar);
657 
658     die(1);			/* as promised */
659 }
660 
661 /*
662  * error - log an error message.
663  */
664 void
665 error __V((char *fmt, ...))
666 {
667     va_list pvar;
668 
669 #if defined(__STDC__)
670     va_start(pvar, fmt);
671 #else
672     char *fmt;
673     va_start(pvar);
674     fmt = va_arg(pvar, char *);
675 #endif
676 
677     logit(LOG_ERR, fmt, pvar);
678     va_end(pvar);
679     ++error_count;
680 }
681 
682 /*
683  * warn - log a warning message.
684  */
685 void
686 warn __V((char *fmt, ...))
687 {
688     va_list pvar;
689 
690 #if defined(__STDC__)
691     va_start(pvar, fmt);
692 #else
693     char *fmt;
694     va_start(pvar);
695     fmt = va_arg(pvar, char *);
696 #endif
697 
698     logit(LOG_WARNING, fmt, pvar);
699     va_end(pvar);
700 }
701 
702 /*
703  * notice - log a notice-level message.
704  */
705 void
706 notice __V((char *fmt, ...))
707 {
708     va_list pvar;
709 
710 #if defined(__STDC__)
711     va_start(pvar, fmt);
712 #else
713     char *fmt;
714     va_start(pvar);
715     fmt = va_arg(pvar, char *);
716 #endif
717 
718     logit(LOG_NOTICE, fmt, pvar);
719     va_end(pvar);
720 }
721 
722 /*
723  * info - log an informational message.
724  */
725 void
726 info __V((char *fmt, ...))
727 {
728     va_list pvar;
729 
730 #if defined(__STDC__)
731     va_start(pvar, fmt);
732 #else
733     char *fmt;
734     va_start(pvar);
735     fmt = va_arg(pvar, char *);
736 #endif
737 
738     logit(LOG_INFO, fmt, pvar);
739     va_end(pvar);
740 }
741 
742 /*
743  * dbglog - log a debug message.
744  */
745 void
746 dbglog __V((char *fmt, ...))
747 {
748     va_list pvar;
749 
750 #if defined(__STDC__)
751     va_start(pvar, fmt);
752 #else
753     char *fmt;
754     va_start(pvar);
755     fmt = va_arg(pvar, char *);
756 #endif
757 
758     logit(LOG_DEBUG, fmt, pvar);
759     va_end(pvar);
760 }
761 
762 /*
763  * dump_packet - print out a packet in readable form if it is interesting.
764  * Assumes len >= PPP_HDRLEN.
765  */
766 void
767 dump_packet(const char *tag, unsigned char *p, int len)
768 {
769     int proto;
770 
771     if (!debug)
772 	return;
773 
774     /*
775      * don't print LCP echo request/reply packets if debug <= 1
776      * and the link is up.
777      */
778     proto = (p[2] << 8) + p[3];
779     if (debug <= 1 && unsuccess == 0 && proto == PPP_LCP
780 	&& len >= PPP_HDRLEN + HEADERLEN) {
781 	unsigned char *lcp = p + PPP_HDRLEN;
782 	int l = (lcp[2] << 8) + lcp[3];
783 
784 	if ((lcp[0] == ECHOREQ || lcp[0] == ECHOREP)
785 	    && l >= HEADERLEN && l <= len - PPP_HDRLEN)
786 	    return;
787     }
788 
789     dbglog("%s %P", tag, p, len);
790 }
791 
792 /*
793  * complete_read - read a full `count' bytes from fd,
794  * unless end-of-file or an error other than EINTR is encountered.
795  */
796 ssize_t
797 complete_read(int fd, void *buf, size_t count)
798 {
799 	size_t done;
800 	ssize_t nb;
801 	char *ptr = buf;
802 
803 	for (done = 0; done < count; ) {
804 		nb = read(fd, ptr, count - done);
805 		if (nb < 0) {
806 			if (errno == EINTR)
807 				continue;
808 			return -1;
809 		}
810 		if (nb == 0)
811 			break;
812 		done += nb;
813 		ptr += nb;
814 	}
815 	return done;
816 }
817 
818 /* Procedures for locking the serial device using a lock file. */
819 #ifndef LOCK_DIR
820 #ifdef __linux__
821 #define LOCK_DIR	"/var/lock"
822 #else
823 #ifdef SVR4
824 #define LOCK_DIR	"/var/spool/locks"
825 #else
826 #define LOCK_DIR	"/var/spool/lock"
827 #endif
828 #endif
829 #endif /* LOCK_DIR */
830 
831 static char lock_file[MAXPATHLEN];
832 
833 /*
834  * lock - create a lock file for the named device
835  */
836 int
837 lock(dev)
838     char *dev;
839 {
840 #ifdef LOCKLIB
841     int result;
842 
843     result = mklock (dev, (void *) 0);
844     if (result == 0) {
845 	strlcpy(lock_file, dev, sizeof(lock_file));
846 	return 0;
847     }
848 
849     if (result > 0)
850         notice("Device %s is locked by pid %d", dev, result);
851     else
852 	error("Can't create lock file %s", lock_file);
853     return -1;
854 
855 #else /* LOCKLIB */
856 
857     char lock_buffer[12];
858     int fd, pid, n;
859 
860 #ifdef SVR4
861     struct stat sbuf;
862 
863     if (stat(dev, &sbuf) < 0) {
864 	error("Can't get device number for %s: %m", dev);
865 	return -1;
866     }
867     if ((sbuf.st_mode & S_IFMT) != S_IFCHR) {
868 	error("Can't lock %s: not a character device", dev);
869 	return -1;
870     }
871     slprintf(lock_file, sizeof(lock_file), "%s/LK.%03d.%03d.%03d",
872 	     LOCK_DIR, major(sbuf.st_dev),
873 	     major(sbuf.st_rdev), minor(sbuf.st_rdev));
874 #else
875     char *p;
876     char lockdev[MAXPATHLEN];
877 
878     if ((p = strstr(dev, "dev/")) != NULL) {
879 	dev = p + 4;
880 	strncpy(lockdev, dev, MAXPATHLEN-1);
881 	lockdev[MAXPATHLEN-1] = 0;
882 	while ((p = strrchr(lockdev, '/')) != NULL) {
883 	    *p = '_';
884 	}
885 	dev = lockdev;
886     } else
887 	if ((p = strrchr(dev, '/')) != NULL)
888 	    dev = p + 1;
889 
890     slprintf(lock_file, sizeof(lock_file), "%s/LCK..%s", LOCK_DIR, dev);
891 #endif
892 
893     while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) {
894 	if (errno != EEXIST) {
895 	    error("Can't create lock file %s: %m", lock_file);
896 	    break;
897 	}
898 
899 	/* Read the lock file to find out who has the device locked. */
900 	fd = open(lock_file, O_RDONLY, 0);
901 	if (fd < 0) {
902 	    if (errno == ENOENT) /* This is just a timing problem. */
903 		continue;
904 	    error("Can't open existing lock file %s: %m", lock_file);
905 	    break;
906 	}
907 #ifndef LOCK_BINARY
908 	n = read(fd, lock_buffer, 11);
909 #else
910 	n = read(fd, &pid, sizeof(pid));
911 #endif /* LOCK_BINARY */
912 	close(fd);
913 	fd = -1;
914 	if (n <= 0) {
915 	    error("Can't read pid from lock file %s", lock_file);
916 	    break;
917 	}
918 
919 	/* See if the process still exists. */
920 #ifndef LOCK_BINARY
921 	lock_buffer[n] = 0;
922 	pid = atoi(lock_buffer);
923 #endif /* LOCK_BINARY */
924 	if (pid == getpid())
925 	    return 1;		/* somebody else locked it for us */
926 	if (pid == 0
927 	    || (kill(pid, 0) == -1 && errno == ESRCH)) {
928 	    if (unlink (lock_file) == 0) {
929 		notice("Removed stale lock on %s (pid %d)", dev, pid);
930 		continue;
931 	    }
932 	    warn("Couldn't remove stale lock on %s", dev);
933 	} else
934 	    notice("Device %s is locked by pid %d", dev, pid);
935 	break;
936     }
937 
938     if (fd < 0) {
939 	lock_file[0] = 0;
940 	return -1;
941     }
942 
943     pid = getpid();
944 #ifndef LOCK_BINARY
945     slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid);
946     write (fd, lock_buffer, 11);
947 #else
948     write(fd, &pid, sizeof (pid));
949 #endif
950     close(fd);
951     return 0;
952 
953 #endif
954 }
955 
956 /*
957  * relock - called to update our lockfile when we are about to detach,
958  * thus changing our pid (we fork, the child carries on, and the parent dies).
959  * Note that this is called by the parent, with pid equal to the pid
960  * of the child.  This avoids a potential race which would exist if
961  * we had the child rewrite the lockfile (the parent might die first,
962  * and another process could think the lock was stale if it checked
963  * between when the parent died and the child rewrote the lockfile).
964  */
965 int
966 relock(pid)
967     int pid;
968 {
969 #ifdef LOCKLIB
970     /* XXX is there a way to do this? */
971     return -1;
972 #else /* LOCKLIB */
973 
974     int fd;
975     char lock_buffer[12];
976 
977     if (lock_file[0] == 0)
978 	return -1;
979     fd = open(lock_file, O_WRONLY, 0);
980     if (fd < 0) {
981 	error("Couldn't reopen lock file %s: %m", lock_file);
982 	lock_file[0] = 0;
983 	return -1;
984     }
985 
986 #ifndef LOCK_BINARY
987     slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid);
988     write (fd, lock_buffer, 11);
989 #else
990     write(fd, &pid, sizeof(pid));
991 #endif /* LOCK_BINARY */
992     close(fd);
993     return 0;
994 
995 #endif /* LOCKLIB */
996 }
997 
998 /*
999  * unlock - remove our lockfile
1000  */
1001 void
1002 unlock()
1003 {
1004     if (lock_file[0]) {
1005 #ifdef LOCKLIB
1006 	(void) rmlock(lock_file, (void *) 0);
1007 #else
1008 	unlink(lock_file);
1009 #endif
1010 	lock_file[0] = 0;
1011     }
1012 }
1013 
1014