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