150594d11e763987fcd3b33180863206cd3b24b9
[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                 /* Skip past NUL padding */
235                 if (*data == '\0') {
236                         data++;
237                         continue;
238                 }
239                 if (strncmp(data, var, vlen) == 0 && data[vlen] == '=') {
240                         p = data + vlen + 1;
241                         break;
242                 }
243                 data += strlen(data) + 1;
244         }
245         if (p != NULL && *p != '\0')
246                 return p;
247         return NULL;
248 }
249
250 const char *
251 dhcpcd_get_value(const DHCPCD_IF *i, const char *var)
252 {
253
254         assert(i);
255         assert(var);
256         return get_value(i->data, i->data_len, var);
257 }
258
259 ssize_t
260 dhcpcd_decode(char *dst, size_t dlen, const char *src)
261 {
262
263         assert(dst);
264         assert(src);
265         return strnunvis(dst, dlen, src);
266 }
267
268 ssize_t
269 dhcpcd_decode_hex(char *dst, size_t dlen, const char *src)
270 {
271         size_t bytes, i;
272         char c;
273         int val, n;
274
275         bytes = 0;
276         while (*src) {
277                 if (dlen == 0 || dlen == 1) {
278                         errno = ENOSPC;
279                         return -1;
280                 }
281                 val = 0;
282                 for (i = 0; i < 2; i++) {
283                         c = *src++;
284                         if (c >= '0' && c <= '9')
285                                 n = c - '0';
286                         else if (c >= 'a' && c <= 'f')
287                                 n = 10 + c - 'a';
288                         else if (c >= 'A' && c <= 'F')
289                                 n = 10 + c - 'A';
290                         else {
291                                 errno = EINVAL;
292                                 return -1;
293                         }
294                         val = val * 16 + n;
295                 }
296                 *dst++ = (char)val;
297                 bytes += 2;
298                 dlen -= 2;
299                 if (*src == ':')
300                         src++;
301         }
302         return (ssize_t)bytes;
303 }
304
305 ssize_t
306 dhcpcd_encode(char *dst, size_t dlen, const char *src, size_t slen)
307 {
308         char *d, c, v[5], *ve, *vp;
309         const char *send;
310
311         d = dst;
312         send = src + slen;
313         while (src < send) {
314                 c = *src++;
315                 ve = vis(v, c, VIS_OCTAL | VIS_CSTYLE, src != send ? *src : 0);
316                 if (dlen < (size_t)(ve - v) + 1) {
317                         errno = ENOSPC;
318                         return -1;
319                 }
320                 dlen -= (size_t)(ve - v);
321                 vp = v;
322                 while (vp != ve)
323                         *d++ = *vp++;
324         }
325         *d = '\0';
326
327         return d - dst;
328 }
329
330 const char *
331 dhcpcd_get_prefix_value(const DHCPCD_IF *i, const char *prefix, const char *var)
332 {
333         char pvar[128], *p;
334         size_t plen, l;
335
336         assert(i);
337         assert(prefix);
338         assert(var);
339
340         p = pvar;
341         plen = sizeof(pvar);
342         l = strlcpy(p, prefix, plen);
343         if (l >= sizeof(pvar)) {
344                 errno = ENOBUFS;
345                 return NULL;
346         }
347         p += l;
348         plen -= l;
349         if (strlcpy(p, var, plen) >= plen) {
350                 errno = ENOBUFS;
351                 return NULL;
352         }
353         return dhcpcd_get_value(i, pvar);
354 }
355
356 static bool
357 strtobool(const char *var)
358 {
359
360         if (var == NULL)
361                 return false;
362
363          return (*var == '0' || *var == '\0' ||
364             strcmp(var, "false") == 0 ||
365             strcmp(var, "no") == 0) ? false : true;
366 }
367
368 static const char *
369 get_status(DHCPCD_CONNECTION *con)
370 {
371         DHCPCD_IF *i;
372         const char *status;
373
374         assert(con);
375         if (con->command_fd == -1)
376                 return "down";
377
378         if (con->listen_fd == -1)
379                 return "opened";
380
381         if (con->interfaces == NULL)
382                 return "initialised";
383
384         status = "disconnected";
385         for (i = con->interfaces; i; i = i->next) {
386                 if (i->up) {
387                         if (strcmp(i->type, "link")) {
388                                 status = "connected";
389                                 break;
390                         } else
391                                 status = "connecting";
392                 }
393         }
394         return status;
395 }
396
397 static void
398 update_status(DHCPCD_CONNECTION *con, const char *nstatus)
399 {
400
401         assert(con);
402         if (nstatus == NULL)
403                 nstatus = get_status(con);
404         if (con->status == NULL || strcmp(nstatus, con->status)) {
405                 con->status = nstatus;
406                 if (con->status_cb)
407                         con->status_cb(con, con->status, con->status_context);
408         }
409 }
410
411 DHCPCD_IF *
412 dhcpcd_interfaces(DHCPCD_CONNECTION *con)
413 {
414
415         assert(con);
416         return con->interfaces;
417 }
418
419 DHCPCD_IF *
420 dhcpcd_get_if(DHCPCD_CONNECTION *con, const char *ifname, const char *type)
421 {
422         DHCPCD_IF *i;
423
424         assert(con);
425         assert(ifname);
426         assert(type);
427
428         for (i = con->interfaces; i; i = i->next)
429                 if (strcmp(i->ifname, ifname) == 0 &&
430                     strcmp(i->type, type) == 0)
431                         return i;
432         return NULL;
433 }
434
435 static DHCPCD_IF *
436 dhcpcd_new_if(DHCPCD_CONNECTION *con, char *data, size_t len)
437 {
438         const char *ifname, *ifclass, *reason, *type, *order, *flags;
439         char *orderdup, *o, *p, *dbuf, *end, *dp, *eq;
440         DHCPCD_IF *e, *i, *l, *n, *nl;
441         int ti;
442         bool addedi;
443         ssize_t dl, el;
444         size_t dbuflen, eql;
445
446         ifname = get_value(data, len, "interface");
447         if (ifname == NULL || *ifname == '\0') {
448                 errno = ESRCH;
449                 return NULL;
450         }
451         reason = get_value(data, len, "reason");
452         if (reason == NULL || *reason == '\0') {
453                 errno = ESRCH;
454                 return NULL;
455         }
456         ifclass = get_value(data, len, "ifclass");
457         /* Skip pseudo interfaces */
458         if (ifclass && *ifclass != '\0') {
459                 errno = ENOTSUP;
460                 return NULL;
461         }
462         if (strcmp(reason, "RECONFIGURE") == 0) {
463                 errno = ENOTSUP;
464                 return NULL;
465         }
466         order = get_value(data, len, "interface_order");
467         if (order == NULL || *order == '\0') {
468                 errno = ESRCH;
469                 return NULL;
470         }
471
472         if (strcmp(reason, "PREINIT") == 0 ||
473             strcmp(reason, "UNKNOWN") == 0 ||
474             strcmp(reason, "CARRIER") == 0 ||
475             strcmp(reason, "NOCARRIER") == 0 ||
476             strcmp(reason, "DEPARTED") == 0 ||
477             strcmp(reason, "STOPPED") == 0)
478                 type = "link";
479         else if (strcmp(reason, "ROUTERADVERT") == 0)
480                 type = "ra";
481         else if (reason[strlen(reason) - 1] == '6')
482                 type = "dhcp6";
483         else
484                 type = "ipv4";
485
486         i = NULL;
487        /* Remove all instances on carrier drop */
488         if (strcmp(reason, "NOCARRIER") == 0 ||
489             strcmp(reason, "DEPARTED") == 0 ||
490             strcmp(reason, "STOPPED") == 0)
491         {
492                 l = NULL;
493                 for (e = con->interfaces; e; e = n) {
494                         n = e->next;
495                         if (strcmp(e->ifname, ifname) == 0) {
496                                 if (strcmp(e->type, type) == 0)
497                                         l = i = e;
498                                 else {
499                                         if (l)
500                                                 l->next = e->next;
501                                         else
502                                                 con->interfaces = e->next;
503                                         free(e);
504                                 }
505                         } else
506                                 l = e;
507                 }
508         } else if (strcmp(type, "link")) {
509                 /* If link is down, ignore it */
510                 e = dhcpcd_get_if(con, ifname, "link");
511                 if (e && !e->up)
512                         return NULL;
513         }
514
515         /* Remove all shell encoding but keep our non graphic encoding */
516         end = data + len;
517         dp = data;
518         dbuflen = 128; /* allocate an initial buffer */
519         dbuf = malloc(dbuflen);
520         if (dbuf == NULL)
521                 return NULL;
522         while (dp < end) {
523                 eq = strchr(dp, '=');
524                 if (eq == NULL || *++eq == '\0') {
525                         errno = EINVAL;
526                         return NULL;
527                 }
528                 eql = strlen(eq) + 1;
529                 if (dbuflen < eql) {
530                         char *nbuf;
531
532                         nbuf = realloc(dbuf, eql);
533                         if (nbuf == NULL) {
534                                 free(dbuf);
535                                 return NULL;
536                         }
537                         dbuf = nbuf;
538                         dbuflen = eql;
539                 }
540                 if ((dl = dhcpcd_decode(dbuf, dbuflen, eq)) == -1 ||
541                     (el = dhcpcd_encode(eq, eql, dbuf, (size_t)dl)) == -1)
542                 {
543                         free(dbuf);
544                         return NULL;
545                 }
546                 /* NUL pad the remainder so get_value can skip it */
547                 if (eql - (size_t)el > 0)
548                         memset(eq + el, 0, eql - (size_t)el);
549                 dp = eq + eql;
550         }
551         free(dbuf);
552
553         orderdup = strdup(order);
554         if (orderdup == NULL)
555                 return NULL;
556
557         /* Find our pointer */
558         if (i == NULL) {
559                 l = NULL;
560                 for (e = con->interfaces; e; e = e->next) {
561                         if (strcmp(e->ifname, ifname) == 0 &&
562                             strcmp(e->type, type) == 0)
563                         {
564                                 i = e;
565                                 break;
566                         }
567                         l = e;
568                 }
569         }
570         if (i == NULL) {
571                 i = malloc(sizeof(*i));
572                 if (i == NULL) {
573                         free(orderdup);
574                         return NULL;
575                 }
576                 if (l)
577                         l->next = i;
578                 else
579                         con->interfaces = i;
580                 i->next = NULL;
581                 i->last_message = NULL;
582         } else
583                 free(i->data);
584
585         /* Now fill out our interface structure */
586         i->con = con;
587         i->data = data;
588         i->data_len = len;
589         i->ifname = ifname;
590         i->type = type;
591         i->reason = reason;
592         flags = dhcpcd_get_value(i, "ifflags");
593         if (flags)
594                 i->flags = (unsigned int)strtoul(flags, NULL, 0);
595         else
596                 i->flags = 0;
597         if (strcmp(reason, "CARRIER") == 0)
598                 i->up = true;
599         else
600                 i->up = strtobool(dhcpcd_get_value(i, "if_up"));
601         i->wireless = strtobool(dhcpcd_get_value(i, "ifwireless"));
602         if (strcmp(i->type, "link") == 0)
603                 i->ssid = dhcpcd_get_value(i, i->up ? "new_ssid" : "old_ssid");
604         else
605                 i->ssid = dhcpcd_get_value(i, "if_ssid");
606
607        /* Sort! */
608         n = nl = NULL;
609         p = orderdup;
610         addedi = false;
611         while ((o = strsep(&p, " ")) != NULL) {
612                 for (ti = 0; dhcpcd_types[ti]; ti++) {
613                         l = NULL;
614                         for (e = con->interfaces; e; e = e->next) {
615                                 if (strcmp(e->ifname, o) == 0 &&
616                                     strcmp(e->type, dhcpcd_types[ti]) == 0)
617                                         break;
618                                 l = e;
619                         }
620                         if (e == NULL)
621                                 continue;
622                         if (i == e)
623                                 addedi = true;
624                         if (l)
625                                 l->next = e->next;
626                         else
627                                 con->interfaces = e->next;
628                         e->next = NULL;
629                         if (nl == NULL)
630                                 n = nl = e;
631                         else {
632                                 nl->next = e;
633                                 nl = e;
634                         }
635                 }
636         }
637         free(orderdup);
638         /* Free any stragglers */
639         while (con->interfaces) {
640                 e = con->interfaces->next;
641                 free(con->interfaces->data);
642                 free(con->interfaces->last_message);
643                 free(con->interfaces);
644                 con->interfaces = e;
645         }
646         con->interfaces = n;
647
648         return addedi ? i : NULL;
649 }
650
651 static DHCPCD_IF *
652 dhcpcd_read_if(DHCPCD_CONNECTION *con, int fd)
653 {
654         char sbuf[sizeof(size_t)], *rbuf;
655         size_t len;
656         ssize_t bytes;
657         DHCPCD_IF *i;
658
659         bytes = read(fd, sbuf, sizeof(sbuf));
660         if (bytes == 0 || bytes == -1) {
661                 dhcpcd_close(con);
662                 return NULL;
663         }
664         memcpy(&len, sbuf, sizeof(len));
665         rbuf = malloc(len + 1);
666         if (rbuf == NULL)
667                 return NULL;
668         bytes = read(fd, rbuf, len);
669         if (bytes == 0 || bytes == -1) {
670                 free(rbuf);
671                 dhcpcd_close(con);
672                 return NULL;
673         }
674         if ((size_t)bytes != len) {
675                 free(rbuf);
676                 errno = EINVAL;
677                 return NULL;
678         }
679         rbuf[bytes] = '\0';
680
681         i = dhcpcd_new_if(con, rbuf, len);
682         if (i == NULL)
683                 free(rbuf);
684         return i;
685 }
686
687 static void
688 dhcpcd_dispatchif(DHCPCD_IF *i)
689 {
690
691         assert(i);
692         if (i->con->if_cb)
693                 i->con->if_cb(i, i->con->if_context);
694         dhcpcd_wpa_if_event(i);
695 }
696
697 void
698 dhcpcd_dispatch(DHCPCD_CONNECTION *con)
699 {
700         DHCPCD_IF *i;
701
702         assert(con);
703         i = dhcpcd_read_if(con, con->listen_fd);
704         if (i)
705                 dhcpcd_dispatchif(i);
706
707         /* Have to call update_status last as it could
708          * cause the interface to be destroyed. */
709         update_status(con, NULL);
710 }
711
712 DHCPCD_CONNECTION *
713 dhcpcd_new(void)
714 {
715         DHCPCD_CONNECTION *con;
716
717         con = calloc(1, sizeof(*con));
718         con->command_fd = con->listen_fd = -1;
719         con->open = false;
720         con->progname = "libdhcpcd";
721         return con;
722 }
723
724 void
725 dhcpcd_set_progname(DHCPCD_CONNECTION *con, const char *progname)
726 {
727
728         assert(con);
729         con->progname = progname;
730 }
731
732 const char *
733 dhcpcd_get_progname(const DHCPCD_CONNECTION *con)
734 {
735
736         assert(con);
737         return con->progname;
738 }
739
740 #ifndef HAVE_STRVERSCMP
741 /* Good enough for our needs */
742 static int
743 strverscmp(const char *s1, const char *s2)
744 {
745         int s1maj, s1min, s1part;
746         int s2maj, s2min, s2part;
747         int r;
748
749         s1min = s1part = 0;
750         if (sscanf(s1, "%d.%d.%d", &s1maj, &s1min, &s1part) < 1)
751                 return -1;
752         s2min = s2part = 0;
753         if (sscanf(s2, "%d.%d.%d", &s2maj, &s2min, &s2part) < 1)
754                 return -1;
755         r = s1maj - s2maj;
756         if (r != 0)
757                 return r;
758         r = s1min - s2min;
759         if (r != 0)
760                 return r;
761         return s1part - s2part;
762 }
763 #endif
764
765 int
766 dhcpcd_open(DHCPCD_CONNECTION *con, bool privileged)
767 {
768         const char *path = privileged ? DHCPCD_SOCKET : DHCPCD_UNPRIV_SOCKET;
769         char cmd[128];
770         ssize_t bytes;
771         size_t nifs, n;
772
773         assert(con);
774         if (con->open) {
775                 if (con->listen_fd != -1)
776                         return con->listen_fd;
777                 errno = EISCONN;
778                 return -1;
779         }
780         /* We need to block the command fd */
781         con->command_fd = dhcpcd_connect(path, 0);
782         if (con->command_fd == -1)
783                 goto err_exit;
784
785         con->terminate_commands = false;
786         if (dhcpcd_ctrl_command(con, "--version", &con->version) <= 0)
787                 goto err_exit;
788         con->terminate_commands =
789             strverscmp(con->version, "6.4.1") >= 0 ? true : false;
790
791         if (dhcpcd_ctrl_command(con, "--getconfigfile", &con->cffile) <= 0)
792                 goto err_exit;
793
794         con->open = true;
795         con->privileged = privileged;
796         update_status(con, NULL);
797
798         con->listen_fd = dhcpcd_connect(path, SOCK_NONBLOCK);
799         if (con->listen_fd == -1)
800                 goto err_exit;
801
802         dhcpcd_command_fd(con, con->listen_fd, false, "--listen", NULL);
803         dhcpcd_command_fd(con, con->command_fd, false, "--getinterfaces", NULL);
804         bytes = read(con->command_fd, cmd, sizeof(nifs));
805         if (bytes != sizeof(nifs))
806                 goto err_exit;
807         memcpy(&nifs, cmd, sizeof(nifs));
808         /* We don't dispatch each interface here as that
809          * causes too much notification spam when the GUI starts */
810         for (n = 0; n < nifs; n++)
811                 dhcpcd_read_if(con, con->command_fd);
812
813         update_status(con, NULL);
814
815         return con->listen_fd;
816
817 err_exit:
818         dhcpcd_close(con);
819         return -1;
820 }
821
822 int
823 dhcpcd_get_fd(DHCPCD_CONNECTION *con)
824 {
825
826         assert(con);
827         return con->listen_fd;
828 }
829
830 bool
831 dhcpcd_privileged(DHCPCD_CONNECTION *con)
832 {
833
834         assert(con);
835         return con->privileged;
836 }
837
838 const char *
839 dhcpcd_status(DHCPCD_CONNECTION *con)
840 {
841
842         assert(con);
843         return con->status;
844 }
845
846 const char *
847 dhcpcd_version(DHCPCD_CONNECTION *con)
848 {
849
850         assert(con);
851         return con->version;
852 }
853
854 const char *
855 dhcpcd_cffile(DHCPCD_CONNECTION *con)
856 {
857
858         assert(con);
859         return con->cffile;
860 }
861
862 void
863 dhcpcd_set_if_callback(DHCPCD_CONNECTION *con,
864     void (*cb)(DHCPCD_IF *, void *), void *ctx)
865 {
866
867         assert(con);
868         con->if_cb = cb;
869         con->if_context = ctx;
870 }
871
872 void
873 dhcpcd_set_status_callback(DHCPCD_CONNECTION *con,
874     void (*cb)(DHCPCD_CONNECTION *, const char *, void *), void *ctx)
875 {
876
877         assert(con);
878         con->status_cb = cb;
879         con->status_context = ctx;
880 }
881
882 void
883 dhcpcd_close(DHCPCD_CONNECTION *con)
884 {
885         DHCPCD_IF *nif;
886         DHCPCD_WPA *nwpa;
887         DHCPCD_WI_HIST *nh;
888
889         assert(con);
890
891         con->open = false;
892
893         /* Shut down WPA listeners as they aren't much good without dhcpcd.
894          * They'll be restarted anyway when dhcpcd comes back up. */
895         while (con->wpa) {
896                 nwpa = con->wpa->next;
897                 dhcpcd_wpa_close(con->wpa);
898                 free(con->wpa);
899                 con->wpa = nwpa;
900         }
901         while (con->wi_history) {
902                 nh = con->wi_history->next;
903                 free(con->wi_history);
904                 con->wi_history = nh;
905         }
906         while (con->interfaces) {
907                 nif = con->interfaces->next;
908                 free(con->interfaces->data);
909                 free(con->interfaces->last_message);
910                 free(con->interfaces);
911                 con->interfaces = nif;
912         }
913
914         if (con->command_fd != -1)
915                 shutdown(con->command_fd, SHUT_RDWR);
916         if (con->listen_fd != -1)
917                 shutdown(con->listen_fd, SHUT_RDWR);
918
919         update_status(con, "down");
920
921         if (con->command_fd != -1) {
922                 close(con->command_fd);
923                 con->command_fd = -1;
924         }
925         if (con->listen_fd != -1) {
926                 close(con->listen_fd);
927                 con->listen_fd = -1;
928         }
929
930         if (con->cffile) {
931                 free(con->cffile);
932                 con->cffile = NULL;
933         }
934         if (con->version) {
935                 free(con->version);
936                 con->version = NULL;
937         }
938         if (con->buf) {
939                 free(con->buf);
940                 con->buf = NULL;
941                 con->buflen = 0;
942         }
943 }
944
945 void
946 dhcpcd_free(DHCPCD_CONNECTION *con)
947 {
948
949         assert(con);
950         free(con);
951 }
952
953 DHCPCD_CONNECTION *
954 dhcpcd_if_connection(DHCPCD_IF *i)
955 {
956
957         assert(i);
958         return i->con;
959 }
960
961 char *
962 dhcpcd_if_message(DHCPCD_IF *i, bool *new_msg)
963 {
964         const char *ip, *iplen, *pfx;
965         char *msg, *p;
966         const char *reason = NULL;
967         size_t len;
968         bool showssid;
969
970         assert(i);
971         /* Don't report non SLAAC configurations */
972         if (strcmp(i->type, "ra") == 0 && i->up &&
973             dhcpcd_get_value(i, "ra1_prefix") == NULL)
974                 return NULL;
975
976         showssid = false;
977         if (strcmp(i->reason, "EXPIRE") == 0)
978                 reason = _("Expired");
979         else if (strcmp(i->reason, "CARRIER") == 0) {
980                 if (i->wireless) {
981                         showssid = true;
982                         reason = _("Associated with");
983                 } else
984                         reason = _("Cable plugged in");
985         } else if (strcmp(i->reason, "NOCARRIER") == 0) {
986                 if (i->wireless) {
987                         if (i->ssid) {
988                                 reason = _("Disassociated from");
989                                 showssid = true;
990                         } else
991                                 reason = _("Not associated");
992                 } else
993                         reason = _("Cable unplugged");
994         } else if (strcmp(i->reason, "DEPARTED") == 0)
995                 reason = _("Departed");
996         else if (strcmp(i->reason, "UNKNOWN") == 0)
997                 reason = _("Unknown link state");
998         else if (strcmp(i->reason, "FAIL") == 0)
999                 reason = _("Automatic configuration not possible");
1000         else if (strcmp(i->reason, "3RDPARTY") == 0)
1001                 reason = _("Waiting for 3rd Party configuration");
1002
1003         if (reason == NULL) {
1004                 if (i->up)
1005                         reason = _("Configured");
1006                 else if (strcmp(i->type, "ra") == 0)
1007                         reason = "Expired RA";
1008                 else
1009                         reason = i->reason;
1010         }
1011
1012         pfx = i->up ? "new_" : "old_";
1013         if ((ip = dhcpcd_get_prefix_value(i, pfx, "ip_address")))
1014                 iplen = dhcpcd_get_prefix_value(i, pfx, "subnet_cidr");
1015         else if ((ip = dhcpcd_get_value(i, "ra1_prefix")))
1016                 iplen = NULL;
1017         else if ((ip = dhcpcd_get_prefix_value(i, pfx,
1018             "dhcp6_ia_na1_ia_addr1")))
1019                 iplen = "128";
1020         else {
1021                 ip = NULL;
1022                 iplen = NULL;
1023         }
1024
1025         len = strlen(i->ifname) + strlen(reason) + 3;
1026         if (showssid && i->ssid)
1027                 len += strlen(i->ssid) + 1;
1028         if (ip)
1029                 len += strlen(ip) + 1;
1030         if (iplen)
1031                 len += strlen(iplen) + 1;
1032         msg = p = malloc(len);
1033         if (msg == NULL)
1034                 return NULL;
1035         p += snprintf(msg, len, "%s: %s", i->ifname, reason);
1036         if (showssid)
1037                 p += snprintf(p, len - (size_t)(p - msg), " %s", i->ssid);
1038         if (iplen)
1039                 snprintf(p, len - (size_t)(p - msg), " %s/%s", ip, iplen);
1040         else if (ip)
1041                 snprintf(p, len - (size_t)(p - msg), " %s", ip);
1042
1043         if (new_msg) {
1044                 if (i->last_message == NULL || strcmp(i->last_message, msg))
1045                         *new_msg = true;
1046                 else
1047                         *new_msg = false;
1048         }
1049         free(i->last_message);
1050         i->last_message = strdup(msg);
1051
1052         return msg;
1053 }