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