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