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