153296c2cda813cc867e429b536665860ba37dd3
[dhcpcd-ui] / src / libdhcpcd / dhcpcd.c
1 /*
2  * libdhcpcd
3  * Copyright 2009-2014 Roy Marples <roy@marples.name>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 // For strverscmp(3)
28 #define _GNU_SOURCE
29
30 #include <sys/socket.h>
31 #include <sys/stat.h>
32 #include <sys/un.h>
33
34 #include <arpa/inet.h>
35
36 #include <assert.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <libintl.h>
41 #include <limits.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 #define IN_LIBDHCPCD
48
49 #include "config.h"
50 #include "dhcpcd.h"
51
52 #ifdef HAS_GETTEXT
53 #include <libintl.h>
54 #define _ gettext
55 #else
56 #define _(a) (a)
57 #endif
58
59 #ifdef HAVE_VIS_H
60 #include <vis.h>
61 #endif
62
63 #ifndef SUN_LEN
64 #define SUN_LEN(su) \
65         (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
66 #endif
67
68 #ifndef iswhite
69 #define iswhite(c)      (c == ' ' || c == '\t' || c == '\n')
70 #endif
71
72 static const char * const dhcpcd_types[] =
73     { "link", "ipv4", "ra", "dhcp6", NULL };
74
75 static ssize_t
76 dhcpcd_command_fd(DHCPCD_CONNECTION *con,
77     int fd, bool progname, const char *cmd, char **buffer)
78 {
79         size_t pl, cl, len;
80         ssize_t bytes;
81         char buf[1024], *p;
82         char *nbuf;
83
84         assert(con);
85         assert(cmd);
86
87         /* Each command is \n terminated.
88          * Each argument is NULL seperated.
89          * We may need to send a space one day, so the API
90          * in this function may need to be improved */
91         cl = strlen(cmd);
92         if (progname) {
93                 pl = strlen(con->progname);
94                 len = pl + 1 + cl + 1;
95         } else {
96                 pl = 0;
97                 len = cl + 1;
98         }
99         if (con->terminate_commands)
100                 len++;
101         if (len > sizeof(buf)) {
102                 errno = ENOBUFS;
103                 return -1;
104         }
105         p = buf;
106         if (progname) {
107                 memcpy(buf, con->progname, pl);
108                 buf[pl] = '\0';
109                 p = buf + pl + 1;
110         }
111         memcpy(p, cmd, cl);
112         p[cl] = '\0';
113         while ((p = strchr(p, ' ')) != NULL)
114                 *p++ = '\0';
115         if (con->terminate_commands) {
116                 buf[len - 2] = '\n';
117                 buf[len - 1] = '\0';
118         } else
119                 buf[len - 1] = '\0';
120         if (write(fd, buf, len) == -1)
121                 return -1;
122         if (buffer == NULL)
123                 return 0;
124
125         bytes = read(fd, buf, sizeof(size_t));
126         if (bytes == 0 || bytes == -1)
127                 return bytes;
128         memcpy(&len, buf, sizeof(size_t));
129         nbuf = realloc(*buffer, len + 1);
130         if (nbuf == NULL)
131                 return -1;
132         *buffer = nbuf;
133         bytes = read(fd, *buffer, len);
134         if (bytes != -1 && (size_t)bytes < len)
135                 *buffer[bytes] = '\0';
136         return bytes;
137 }
138
139 ssize_t
140 dhcpcd_command(DHCPCD_CONNECTION *con, const char *cmd, char **buffer)
141 {
142
143         assert(con);
144         if (!con->privileged) {
145                 errno = EACCES;
146                 return -1;
147         }
148         return dhcpcd_command_fd(con, con->command_fd, true, cmd, buffer);
149 }
150
151 static ssize_t
152 dhcpcd_ctrl_command(DHCPCD_CONNECTION *con, const char *cmd, char **buffer)
153 {
154
155         return dhcpcd_command_fd(con, con->command_fd, false, cmd, buffer);
156 }
157
158 bool
159 dhcpcd_realloc(DHCPCD_CONNECTION *con, size_t len)
160 {
161
162         assert(con);
163         if (con->buflen < len) {
164                 char *nbuf;
165
166                 nbuf = realloc(con->buf, len);
167                 if (nbuf == NULL)
168                         return false;
169                 con->buf = nbuf;
170                 con->buflen = len;
171         }
172         return true;
173 }
174
175 ssize_t
176 dhcpcd_command_arg(DHCPCD_CONNECTION *con, const char *cmd, const char *arg,
177     char **buffer)
178 {
179         size_t cmdlen, len;
180
181         assert(con);
182         assert(cmd);
183
184         cmdlen = strlen(cmd);
185         if (arg)
186                 len = cmdlen + strlen(arg) + 2;
187         else
188                 len = cmdlen + 1;
189         if (!dhcpcd_realloc(con, len))
190                 return -1;
191         strlcpy(con->buf, cmd, con->buflen);
192         if (arg) {
193                 con->buf[cmdlen] = ' ';
194                 strlcpy(con->buf + cmdlen + 1, arg, con->buflen - 1 - cmdlen);
195         }
196
197         return dhcpcd_command_fd(con, con->command_fd, true, con->buf, buffer);
198 }
199
200
201 static int
202 dhcpcd_connect(const char *path, int opts)
203 {
204         int fd;
205         socklen_t len;
206         struct sockaddr_un sun;
207
208         assert(path);
209         fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | opts, 0);
210         if (fd == -1)
211                 return -1;
212
213         memset(&sun, 0, sizeof(sun));
214         sun.sun_family = AF_UNIX;
215         strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
216         len = (socklen_t)SUN_LEN(&sun);
217         if (connect(fd, (struct sockaddr *)&sun, len) == 0)
218                 return fd;
219         close(fd);
220         return -1;
221 }
222
223 static const char *
224 get_value(const char *data, size_t len, const char *var)
225 {
226         const char *end, *p;
227         size_t vlen;
228
229         assert(var);
230         end = data + len;
231         vlen = strlen(var);
232         p = NULL;
233         while (data + vlen + 1 < end) {
234                 if (strncmp(data, var, vlen) == 0) {
235                         p = data + vlen;
236                         break;
237                 }
238                 data += strlen(data) + 1;
239         }
240         if (p != NULL && *p != '\0')
241                 return p;
242         return NULL;
243 }
244
245 const char *
246 dhcpcd_get_value(const DHCPCD_IF *i, const char *var)
247 {
248
249         assert(i);
250         assert(var);
251         return get_value(i->data, i->data_len, var);
252 }
253
254 ssize_t
255 dhcpcd_decode(char *dst, size_t dlen, const char *src)
256 {
257
258         assert(dst);
259         assert(src);
260         return strnunvis(dst, dlen, src);
261 }
262
263 ssize_t
264 dhcpcd_decode_hex(char *dst, size_t dlen, const char *src)
265 {
266         size_t bytes, i;
267         char c;
268         int val, n;
269
270         bytes = 0;
271         while (*src) {
272                 if (dlen == 0 || dlen == 1) {
273                         errno = ENOSPC;
274                         return -1;
275                 }
276                 val = 0;
277                 for (i = 0; i < 2; i++) {
278                         c = *src++;
279                         if (c >= '0' && c <= '9')
280                                 n = c - '0';
281                         else if (c >= 'a' && c <= 'f')
282                                 n = 10 + c - 'a';
283                         else if (c >= 'A' && c <= 'F')
284                                 n = 10 + c - 'A';
285                         else {
286                                 errno = EINVAL;
287                                 return -1;
288                         }
289                         val = val * 16 + n;
290                 }
291                 *dst++ = (char)val;
292                 bytes += 2;
293                 dlen -= 2;
294                 if (*src == ':')
295                         src++;
296         }
297         return (ssize_t)bytes;
298 }
299
300 ssize_t
301 dhcpcd_encode(char *dst, size_t dlen, const char *src, size_t slen)
302 {
303         char *d, c, v[5], *ve, *vp;
304         const char *send;
305
306         d = dst;
307         send = src + slen;
308         while (src < send) {
309                 c = *src++;
310                 ve = vis(v, c, VIS_OCTAL | VIS_CSTYLE, src != send ? *src : 0);
311                 if (dlen < (size_t)(ve - v) + 1) {
312                         errno = ENOSPC;
313                         return -1;
314                 }
315                 dlen -= (size_t)(ve - v);
316                 vp = v;
317                 while (vp != ve)
318                         *d++ = *vp++;
319         }
320         *d = '\0';
321
322         return d - dst;
323 }
324
325 ssize_t
326 dhcpcd_decode_shell(char *dst, size_t dlen, const char *src)
327 {
328         char *tmp;
329         ssize_t l;
330
331         assert(dst);
332         assert(src);
333
334         tmp = malloc(dlen);
335         if (tmp == NULL)
336                 return -1;
337         if ((l = dhcpcd_decode(tmp, dlen, src)) != -1)
338                 l = dhcpcd_encode(dst, dlen, tmp, (size_t)l);
339
340         free(tmp);
341         return l;
342 }
343
344 const char *
345 dhcpcd_get_prefix_value(const DHCPCD_IF *i, const char *prefix, const char *var)
346 {
347         char pvar[128], *p;
348         size_t plen, l;
349
350         assert(i);
351         assert(prefix);
352         assert(var);
353
354         p = pvar;
355         plen = sizeof(pvar);
356         l = strlcpy(p, prefix, plen);
357         if (l >= sizeof(pvar)) {
358                 errno = ENOBUFS;
359                 return NULL;
360         }
361         p += l;
362         plen -= l;
363         if (strlcpy(p, var, plen) >= plen) {
364                 errno = ENOBUFS;
365                 return NULL;
366         }
367         return dhcpcd_get_value(i, pvar);
368 }
369
370 static bool
371 strtobool(const char *var)
372 {
373
374         if (var == NULL)
375                 return false;
376
377          return (*var == '0' || *var == '\0' ||
378             strcmp(var, "false") == 0 ||
379             strcmp(var, "no") == 0) ? false : true;
380 }
381
382 static const char *
383 get_status(DHCPCD_CONNECTION *con)
384 {
385         DHCPCD_IF *i;
386         const char *status;
387
388         assert(con);
389         if (con->command_fd == -1)
390                 return "down";
391
392         if (con->listen_fd == -1)
393                 return "opened";
394
395         if (con->interfaces == NULL)
396                 return "initialised";
397
398         status = "disconnected";
399         for (i = con->interfaces; i; i = i->next) {
400                 if (i->up) {
401                         if (strcmp(i->type, "link")) {
402                                 status = "connected";
403                                 break;
404                         } else
405                                 status = "connecting";
406                 }
407         }
408         return status;
409 }
410
411 static void
412 update_status(DHCPCD_CONNECTION *con, const char *nstatus)
413 {
414
415         assert(con);
416         if (nstatus == NULL)
417                 nstatus = get_status(con);
418         if (con->status == NULL || strcmp(nstatus, con->status)) {
419                 con->status = nstatus;
420                 if (con->status_cb)
421                         con->status_cb(con, con->status, con->status_context);
422         }
423 }
424
425 DHCPCD_IF *
426 dhcpcd_interfaces(DHCPCD_CONNECTION *con)
427 {
428
429         assert(con);
430         return con->interfaces;
431 }
432
433 DHCPCD_IF *
434 dhcpcd_get_if(DHCPCD_CONNECTION *con, const char *ifname, const char *type)
435 {
436         DHCPCD_IF *i;
437
438         assert(con);
439         assert(ifname);
440         assert(type);
441
442         for (i = con->interfaces; i; i = i->next)
443                 if (strcmp(i->ifname, ifname) == 0 &&
444                     strcmp(i->type, type) == 0)
445                         return i;
446         return NULL;
447 }
448
449 static DHCPCD_IF *
450 dhcpcd_new_if(DHCPCD_CONNECTION *con, char *data, size_t len)
451 {
452         const char *ifname, *ifclass, *reason, *type, *order, *flags, *ssid;
453         char *orderdup, *o, *p;
454         DHCPCD_IF *e, *i, *l, *n, *nl;
455         int ti;
456         bool addedi;
457
458         ifname = get_value(data, len, "interface=");
459         if (ifname == NULL || *ifname == '\0') {
460                 errno = ESRCH;
461                 return NULL;
462         }
463         reason = get_value(data, len, "reason=");
464         if (reason == NULL || *reason == '\0') {
465                 errno = ESRCH;
466                 return NULL;
467         }
468         ifclass = get_value(data, len, "ifclass=");
469         /* Skip pseudo interfaces */
470         if (ifclass && *ifclass != '\0') {
471                 errno = ENOTSUP;
472                 return NULL;
473         }
474         if (strcmp(reason, "RECONFIGURE") == 0) {
475                 errno = ENOTSUP;
476                 return NULL;
477         }
478         order = get_value(data, len, "interface_order=");
479         if (order == NULL || *order == '\0') {
480                 errno = ESRCH;
481                 return NULL;
482         }
483
484         if (strcmp(reason, "PREINIT") == 0 ||
485             strcmp(reason, "UNKNOWN") == 0 ||
486             strcmp(reason, "CARRIER") == 0 ||
487             strcmp(reason, "NOCARRIER") == 0 ||
488             strcmp(reason, "DEPARTED") == 0 ||
489             strcmp(reason, "STOPPED") == 0)
490                 type = "link";
491         else if (strcmp(reason, "ROUTERADVERT") == 0)
492                 type = "ra";
493         else if (reason[strlen(reason) - 1] == '6')
494                 type = "dhcp6";
495         else
496                 type = "ipv4";
497
498         i = NULL;
499        /* Remove all instances on carrier drop */
500         if (strcmp(reason, "NOCARRIER") == 0 ||
501             strcmp(reason, "DEPARTED") == 0 ||
502             strcmp(reason, "STOPPED") == 0)
503         {
504                 l = NULL;
505                 for (e = con->interfaces; e; e = n) {
506                         n = e->next;
507                         if (strcmp(e->ifname, ifname) == 0) {
508                                 if (strcmp(e->type, type) == 0)
509                                         l = i = e;
510                                 else {
511                                         if (l)
512                                                 l->next = e->next;
513                                         else
514                                                 con->interfaces = e->next;
515                                         free(e);
516                                 }
517                         } else
518                                 l = e;
519                 }
520         } else if (strcmp(type, "link")) {
521                 /* If link is down, ignore it */
522                 e = dhcpcd_get_if(con, ifname, "link");
523                 if (e && !e->up)
524                         return NULL;
525         }
526
527         orderdup = strdup(order);
528         if (orderdup == NULL)
529                 return NULL;
530
531         /* Find our pointer */
532         if (i == NULL) {
533                 l = NULL;
534                 for (e = con->interfaces; e; e = e->next) {
535                         if (strcmp(e->ifname, ifname) == 0 &&
536                             strcmp(e->type, type) == 0)
537                         {
538                                 i = e;
539                                 break;
540                         }
541                         l = e;
542                 }
543         }
544         if (i == NULL) {
545                 i = malloc(sizeof(*i));
546                 if (i == NULL) {
547                         free(orderdup);
548                         return NULL;
549                 }
550                 if (l)
551                         l->next = i;
552                 else
553                         con->interfaces = i;
554                 i->next = NULL;
555                 i->last_message = NULL;
556         } else
557                 free(i->data);
558
559         /* Now fill out our interface structure */
560         i->con = con;
561         i->data = data;
562         i->data_len = len;
563         i->ifname = ifname;
564         i->type = type;
565         i->reason = reason;
566         flags = dhcpcd_get_value(i, "ifflags=");
567         if (flags)
568                 i->flags = (unsigned int)strtoul(flags, NULL, 0);
569         else
570                 i->flags = 0;
571         if (strcmp(reason, "CARRIER") == 0)
572                 i->up = true;
573         else
574                 i->up = strtobool(dhcpcd_get_value(i, "if_up="));
575         i->wireless = strtobool(dhcpcd_get_value(i, "ifwireless="));
576         ssid = dhcpcd_get_value(i, i->up ? "new_ssid=" : "old_ssid=");
577         if (ssid == NULL ||
578             dhcpcd_decode_shell(i->ssid, sizeof(i->ssid), ssid) == -1)
579                 *i->ssid = '\0';
580
581        /* Sort! */
582         n = nl = NULL;
583         p = orderdup;
584         addedi = false;
585         while ((o = strsep(&p, " ")) != NULL) {
586                 for (ti = 0; dhcpcd_types[ti]; ti++) {
587                         l = NULL;
588                         for (e = con->interfaces; e; e = e->next) {
589                                 if (strcmp(e->ifname, o) == 0 &&
590                                     strcmp(e->type, dhcpcd_types[ti]) == 0)
591                                         break;
592                                 l = e;
593                         }
594                         if (e == NULL)
595                                 continue;
596                         if (i == e)
597                                 addedi = true;
598                         if (l)
599                                 l->next = e->next;
600                         else
601                                 con->interfaces = e->next;
602                         e->next = NULL;
603                         if (nl == NULL)
604                                 n = nl = e;
605                         else {
606                                 nl->next = e;
607                                 nl = e;
608                         }
609                 }
610         }
611         free(orderdup);
612         /* Free any stragglers */
613         while (con->interfaces) {
614                 e = con->interfaces->next;
615                 free(con->interfaces->data);
616                 free(con->interfaces->last_message);
617                 free(con->interfaces);
618                 con->interfaces = e;
619         }
620         con->interfaces = n;
621
622         return addedi ? i : NULL;
623 }
624
625 static DHCPCD_IF *
626 dhcpcd_read_if(DHCPCD_CONNECTION *con, int fd)
627 {
628         char sbuf[sizeof(size_t)], *rbuf;
629         size_t len;
630         ssize_t bytes;
631         DHCPCD_IF *i;
632
633         bytes = read(fd, sbuf, sizeof(sbuf));
634         if (bytes == 0 || bytes == -1) {
635                 dhcpcd_close(con);
636                 return NULL;
637         }
638         memcpy(&len, sbuf, sizeof(len));
639         rbuf = malloc(len + 1);
640         if (rbuf == NULL)
641                 return NULL;
642         bytes = read(fd, rbuf, len);
643         if (bytes == 0 || bytes == -1) {
644                 free(rbuf);
645                 dhcpcd_close(con);
646                 return NULL;
647         }
648         if ((size_t)bytes != len) {
649                 free(rbuf);
650                 errno = EINVAL;
651                 return NULL;
652         }
653         rbuf[bytes] = '\0';
654
655         i = dhcpcd_new_if(con, rbuf, len);
656         if (i == NULL)
657                 free(rbuf);
658         return i;
659 }
660
661 static void
662 dhcpcd_dispatchif(DHCPCD_IF *i)
663 {
664
665         assert(i);
666         if (i->con->if_cb)
667                 i->con->if_cb(i, i->con->if_context);
668         dhcpcd_wpa_if_event(i);
669 }
670
671 void
672 dhcpcd_dispatch(DHCPCD_CONNECTION *con)
673 {
674         DHCPCD_IF *i;
675
676         assert(con);
677         i = dhcpcd_read_if(con, con->listen_fd);
678         if (i)
679                 dhcpcd_dispatchif(i);
680
681         /* Have to call update_status last as it could
682          * cause the interface to be destroyed. */
683         update_status(con, NULL);
684 }
685
686 DHCPCD_CONNECTION *
687 dhcpcd_new(void)
688 {
689         DHCPCD_CONNECTION *con;
690
691         con = calloc(1, sizeof(*con));
692         con->command_fd = con->listen_fd = -1;
693         con->open = false;
694         con->progname = "libdhcpcd";
695         return con;
696 }
697
698 void
699 dhcpcd_set_progname(DHCPCD_CONNECTION *con, const char *progname)
700 {
701
702         assert(con);
703         con->progname = progname;
704 }
705
706 const char *
707 dhcpcd_get_progname(const DHCPCD_CONNECTION *con)
708 {
709
710         assert(con);
711         return con->progname;
712 }
713
714 #ifndef HAVE_STRVERSCMP
715 /* Good enough for our needs */
716 static int
717 strverscmp(const char *s1, const char *s2)
718 {
719         int s1maj, s1min, s1part;
720         int s2maj, s2min, s2part;
721         int r;
722
723         s1min = s1part = 0;
724         if (sscanf(s1, "%d.%d.%d", &s1maj, &s1min, &s1part) < 1)
725                 return -1;
726         s2min = s2part = 0;
727         if (sscanf(s2, "%d.%d.%d", &s2maj, &s2min, &s2part) < 1)
728                 return -1;
729         r = s1maj - s2maj;
730         if (r != 0)
731                 return r;
732         r = s1min - s2min;
733         if (r != 0)
734                 return r;
735         return s1part - s2part;
736 }
737 #endif
738
739 int
740 dhcpcd_open(DHCPCD_CONNECTION *con, bool privileged)
741 {
742         const char *path = privileged ? DHCPCD_SOCKET : DHCPCD_UNPRIV_SOCKET;
743         char cmd[128];
744         ssize_t bytes;
745         size_t nifs, n;
746
747         assert(con);
748         if (con->open) {
749                 if (con->listen_fd != -1)
750                         return con->listen_fd;
751                 errno = EISCONN;
752                 return -1;
753         }
754         /* We need to block the command fd */
755         con->command_fd = dhcpcd_connect(path, 0);
756         if (con->command_fd == -1)
757                 goto err_exit;
758
759         con->terminate_commands = false;
760         if (dhcpcd_ctrl_command(con, "--version", &con->version) <= 0)
761                 goto err_exit;
762         con->terminate_commands =
763             strverscmp(con->version, "6.4.1") >= 0 ? true : false;
764
765         if (dhcpcd_ctrl_command(con, "--getconfigfile", &con->cffile) <= 0)
766                 goto err_exit;
767
768         con->open = true;
769         con->privileged = privileged;
770         update_status(con, NULL);
771
772         con->listen_fd = dhcpcd_connect(path, SOCK_NONBLOCK);
773         if (con->listen_fd == -1)
774                 goto err_exit;
775
776         dhcpcd_command_fd(con, con->listen_fd, false, "--listen", NULL);
777         dhcpcd_command_fd(con, con->command_fd, false, "--getinterfaces", NULL);
778         bytes = read(con->command_fd, cmd, sizeof(nifs));
779         if (bytes != sizeof(nifs))
780                 goto err_exit;
781         memcpy(&nifs, cmd, sizeof(nifs));
782         /* We don't dispatch each interface here as that
783          * causes too much notification spam when the GUI starts */
784         for (n = 0; n < nifs; n++)
785                 dhcpcd_read_if(con, con->command_fd);
786
787         update_status(con, NULL);
788
789         return con->listen_fd;
790
791 err_exit:
792         dhcpcd_close(con);
793         return -1;
794 }
795
796 int
797 dhcpcd_get_fd(DHCPCD_CONNECTION *con)
798 {
799
800         assert(con);
801         return con->listen_fd;
802 }
803
804 bool
805 dhcpcd_privileged(DHCPCD_CONNECTION *con)
806 {
807
808         assert(con);
809         return con->privileged;
810 }
811
812 const char *
813 dhcpcd_status(DHCPCD_CONNECTION *con)
814 {
815
816         assert(con);
817         return con->status;
818 }
819
820 const char *
821 dhcpcd_version(DHCPCD_CONNECTION *con)
822 {
823
824         assert(con);
825         return con->version;
826 }
827
828 const char *
829 dhcpcd_cffile(DHCPCD_CONNECTION *con)
830 {
831
832         assert(con);
833         return con->cffile;
834 }
835
836 void
837 dhcpcd_set_if_callback(DHCPCD_CONNECTION *con,
838     void (*cb)(DHCPCD_IF *, void *), void *ctx)
839 {
840
841         assert(con);
842         con->if_cb = cb;
843         con->if_context = ctx;
844 }
845
846 void
847 dhcpcd_set_status_callback(DHCPCD_CONNECTION *con,
848     void (*cb)(DHCPCD_CONNECTION *, const char *, void *), void *ctx)
849 {
850
851         assert(con);
852         con->status_cb = cb;
853         con->status_context = ctx;
854 }
855
856 void
857 dhcpcd_close(DHCPCD_CONNECTION *con)
858 {
859         DHCPCD_IF *nif;
860         DHCPCD_WPA *nwpa;
861         DHCPCD_WI_HIST *nh;
862
863         assert(con);
864
865         con->open = false;
866
867         /* Shut down WPA listeners as they aren't much good without dhcpcd.
868          * They'll be restarted anyway when dhcpcd comes back up. */
869         while (con->wpa) {
870                 nwpa = con->wpa->next;
871                 dhcpcd_wpa_close(con->wpa);
872                 free(con->wpa);
873                 con->wpa = nwpa;
874         }
875         while (con->wi_history) {
876                 nh = con->wi_history->next;
877                 free(con->wi_history);
878                 con->wi_history = nh;
879         }
880         while (con->interfaces) {
881                 nif = con->interfaces->next;
882                 free(con->interfaces->data);
883                 free(con->interfaces->last_message);
884                 free(con->interfaces);
885                 con->interfaces = nif;
886         }
887
888         if (con->command_fd != -1)
889                 shutdown(con->command_fd, SHUT_RDWR);
890         if (con->listen_fd != -1)
891                 shutdown(con->listen_fd, SHUT_RDWR);
892
893         update_status(con, "down");
894
895         if (con->command_fd != -1) {
896                 close(con->command_fd);
897                 con->command_fd = -1;
898         }
899         if (con->listen_fd != -1) {
900                 close(con->listen_fd);
901                 con->listen_fd = -1;
902         }
903
904         if (con->cffile) {
905                 free(con->cffile);
906                 con->cffile = NULL;
907         }
908         if (con->version) {
909                 free(con->version);
910                 con->version = NULL;
911         }
912         if (con->buf) {
913                 free(con->buf);
914                 con->buf = NULL;
915                 con->buflen = 0;
916         }
917 }
918
919 void
920 dhcpcd_free(DHCPCD_CONNECTION *con)
921 {
922
923         assert(con);
924         free(con);
925 }
926
927 DHCPCD_CONNECTION *
928 dhcpcd_if_connection(DHCPCD_IF *i)
929 {
930
931         assert(i);
932         return i->con;
933 }
934
935 char *
936 dhcpcd_if_message(DHCPCD_IF *i, bool *new_msg)
937 {
938         const char *ip, *iplen, *pfx;
939         char *msg, *p;
940         const char *reason = NULL;
941         size_t len;
942         bool showssid;
943
944         assert(i);
945         /* Don't report non SLAAC configurations */
946         if (strcmp(i->type, "ra") == 0 && i->up &&
947             dhcpcd_get_value(i, "ra1_prefix=") == NULL)
948                 return NULL;
949
950         showssid = false;
951         if (strcmp(i->reason, "EXPIRE") == 0)
952                 reason = _("Expired");
953         else if (strcmp(i->reason, "CARRIER") == 0) {
954                 if (i->wireless) {
955                         showssid = true;
956                         reason = _("Associated with");
957                 } else
958                         reason = _("Cable plugged in");
959         } else if (strcmp(i->reason, "NOCARRIER") == 0) {
960                 if (i->wireless) {
961                         if (i->ssid) {
962                                 reason = _("Disassociated from");
963                                 showssid = true;
964                         } else
965                                 reason = _("Not associated");
966                 } else
967                         reason = _("Cable unplugged");
968         } else if (strcmp(i->reason, "DEPARTED") == 0)
969                 reason = _("Departed");
970         else if (strcmp(i->reason, "UNKNOWN") == 0)
971                 reason = _("Unknown link state");
972         else if (strcmp(i->reason, "FAIL") == 0)
973                 reason = _("Automatic configuration not possible");
974         else if (strcmp(i->reason, "3RDPARTY") == 0)
975                 reason = _("Waiting for 3rd Party configuration");
976
977         if (reason == NULL) {
978                 if (i->up)
979                         reason = _("Configured");
980                 else if (strcmp(i->type, "ra") == 0)
981                         reason = "Expired RA";
982                 else
983                         reason = i->reason;
984         }
985
986         pfx = i->up ? "new_" : "old_";
987         if ((ip = dhcpcd_get_prefix_value(i, pfx, "ip_address=")))
988                 iplen = dhcpcd_get_prefix_value(i, pfx, "subnet_cidr=");
989         else if ((ip = dhcpcd_get_value(i, "ra1_prefix=")))
990                 iplen = NULL;
991         else if ((ip = dhcpcd_get_prefix_value(i, pfx,
992             "dhcp6_ia_na1_ia_addr1=")))
993                 iplen = "128";
994         else {
995                 ip = NULL;
996                 iplen = NULL;
997         }
998
999         len = strlen(i->ifname) + strlen(reason) + 3;
1000         if (showssid && i->ssid)
1001                 len += strlen(i->ssid) + 1;
1002         if (ip)
1003                 len += strlen(ip) + 1;
1004         if (iplen)
1005                 len += strlen(iplen) + 1;
1006         msg = p = malloc(len);
1007         if (msg == NULL)
1008                 return NULL;
1009         p += snprintf(msg, len, "%s: %s", i->ifname, reason);
1010         if (showssid)
1011                 p += snprintf(p, len - (size_t)(p - msg), " %s", i->ssid);
1012         if (iplen)
1013                 snprintf(p, len - (size_t)(p - msg), " %s/%s", ip, iplen);
1014         else if (ip)
1015                 snprintf(p, len - (size_t)(p - msg), " %s", ip);
1016
1017         if (new_msg) {
1018                 if (i->last_message == NULL || strcmp(i->last_message, msg))
1019                         *new_msg = true;
1020                 else
1021                         *new_msg = false;
1022         }
1023         free(i->last_message);
1024         i->last_message = strdup(msg);
1025
1026         return msg;
1027 }