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