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