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