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