Skip pseudo interfaces
[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         } else
398                 free(i->data);
399
400         /* Now fill out our interface structure */
401         i->con = con;
402         i->data = data;
403         i->data_len = len;
404         i->ifname = ifname;
405         i->type = type;
406         i->reason = reason;
407         flags = dhcpcd_get_value(i, "ifflags=");
408         if (flags)
409                 i->flags = (unsigned int)strtoul(flags, NULL, 0);
410         else
411                 i->flags = 0;
412         if (strcmp(reason, "CARRIER") == 0)
413                 i->up = true;
414         else
415                 i->up = strtobool(dhcpcd_get_value(i, "if_up="));
416         i->wireless = strtobool(dhcpcd_get_value(i, "ifwireless="));
417         i->ssid = dhcpcd_get_value(i, i->up ? "new_ssid=" : "old_ssid=");
418
419        /* Sort! */
420         n = nl = NULL;
421         p = orderdup;
422         while ((o = strsep(&p, " ")) != NULL) {
423                 for (ti = 0; dhcpcd_types[ti]; ti++) {
424                         l = NULL;
425                         for (e = con->interfaces; e; e = e->next) {
426                                 if (strcmp(e->ifname, o) == 0 &&
427                                     strcmp(e->type, dhcpcd_types[ti]) == 0)
428                                         break;
429                                 l = e;
430                         }
431                         if (e == NULL)
432                                 continue;
433                         if (l)
434                                 l->next = e->next;
435                         else
436                                 con->interfaces = e->next;
437                         e->next = NULL;
438                         if (nl == NULL)
439                                 n = nl = e;
440                         else {
441                                 nl->next = e;
442                                 nl = e;
443                         }
444                 }
445         }
446         free(orderdup);
447         /* Free any stragglers */
448         while (con->interfaces) {
449                 e = con->interfaces->next;
450                 free(con->interfaces->data);
451                 free(con->interfaces);
452                 con->interfaces = e;
453         }
454         con->interfaces = n;
455
456         return i;
457 }
458
459 static DHCPCD_IF *
460 dhcpcd_read_if(DHCPCD_CONNECTION *con, int fd)
461 {
462         char sbuf[sizeof(size_t)], *rbuf;
463         size_t len;
464         ssize_t bytes;
465         DHCPCD_IF *i;
466
467         bytes = read(fd, sbuf, sizeof(sbuf));
468         if (bytes == 0 || bytes == -1) {
469                 dhcpcd_close(con);
470                 return NULL;
471         }
472         memcpy(&len, sbuf, sizeof(len));
473         rbuf = malloc(len + 1);
474         if (rbuf == NULL)
475                 return NULL;
476         bytes = read(fd, rbuf, len);
477         if (bytes == 0 || bytes == -1) {
478                 free(rbuf);
479                 dhcpcd_close(con);
480                 return NULL;
481         }
482         if ((size_t)bytes != len) {
483                 free(rbuf);
484                 errno = EINVAL;
485                 return NULL;
486         }
487         rbuf[bytes] = '\0';
488
489         i = dhcpcd_new_if(con, rbuf, len);
490         if (i == NULL)
491                 free(rbuf);
492         return i;
493 }
494
495 static void
496 dhcpcd_dispatchif(DHCPCD_IF *i)
497 {
498
499         assert(i);
500         if (i->con->if_cb)
501                 i->con->if_cb(i, i->con->if_context);
502         dhcpcd_wpa_if_event(i);
503 }
504
505 void
506 dhcpcd_dispatch(DHCPCD_CONNECTION *con)
507 {
508         DHCPCD_IF *i;
509
510         assert(con);
511         i = dhcpcd_read_if(con, con->listen_fd);
512         if (i)
513                 dhcpcd_dispatchif(i);
514
515         /* Have to call update_status last as it could
516          * cause the interface to be destroyed. */
517         update_status(con);
518 }
519
520 DHCPCD_CONNECTION *
521 dhcpcd_new(void)
522 {
523         DHCPCD_CONNECTION *con;
524
525         con = calloc(1, sizeof(*con));
526         con->command_fd = con->listen_fd = -1;
527         return con;
528 }
529
530 #ifndef __GLIBC__
531 /* Good enough for our needs */
532 static int
533 strverscmp(const char *s1, const char *s2)
534 {
535         int s1maj, s1min, s1part;
536         int s2maj, s2min, s2part;
537         int r;
538
539         s1min = s1part = 0;
540         if (sscanf(s1, "%d.%d.%d", &s1maj, &s1min, &s1part) < 1)
541                 return -1;
542         s2min = s2part = 0;
543         if (sscanf(s2, "%d.%d.%d", &s2maj, &s2min, &s2part) < 1)
544                 return -1;
545         r = s1maj - s2maj;
546         if (r != 0)
547                 return r;
548         r = s1min - s2min;
549         if (r != 0)
550                 return r;
551         return s1part - s2part;
552 }
553 #endif
554
555 int
556 dhcpcd_open(DHCPCD_CONNECTION *con)
557 {
558         char cmd[128];
559         ssize_t bytes;
560         size_t nifs, n;
561         DHCPCD_IF *i;
562
563         assert(con);
564         if (con->listen_fd != -1)
565                 return con->listen_fd;
566         con->command_fd = dhcpcd_connect();
567         if (con->command_fd == -1) {
568                 update_status(con);
569                 return -1;
570         }
571
572         con->terminate_commands = false;
573         if (dhcpcd_command(con, "--version", &con->version) <= 0)
574                 return -1;
575         con->terminate_commands =
576             strverscmp(con->version, "6.4.1") >= 0 ? true : false;
577
578         if (dhcpcd_command(con, "--getconfigfile", &con->cffile) <= 0)
579                 return -1;
580
581         con->listen_fd = dhcpcd_connect();
582         if (con->listen_fd == -1) {
583                 close(con->command_fd);
584                 con->command_fd = -1;
585                 update_status(con);
586                 return -1;
587         }
588         dhcpcd_command_fd(con, con->listen_fd, "--listen", NULL);
589
590         dhcpcd_command_fd(con, con->command_fd, "--getinterfaces", NULL);
591         bytes = read(con->command_fd, cmd, sizeof(nifs));
592         if (bytes != sizeof(nifs)) {
593                 close(con->command_fd);
594                 con->command_fd = -1;
595                 close(con->listen_fd);
596                 con->listen_fd = -1;
597         } else {
598                 memcpy(&nifs, cmd, sizeof(nifs));
599                 /* We don't dispatch each interface here as that
600                  * causes too much notification spam when the GUI starts */
601                 for (n = 0; n < nifs; n++) {
602                         i = dhcpcd_read_if(con, con->command_fd);
603                         if (i)
604                                 dhcpcd_wpa_if_event(i);
605                 }
606         }
607         update_status(con);
608
609         return con->listen_fd;
610 }
611
612 int
613 dhcpcd_get_fd(DHCPCD_CONNECTION *con)
614 {
615
616         assert(con);
617         return con->listen_fd;
618 }
619
620 const char *
621 dhcpcd_status(DHCPCD_CONNECTION *con)
622 {
623
624         assert(con);
625         return con->status;
626 }
627
628 const char *
629 dhcpcd_version(DHCPCD_CONNECTION *con)
630 {
631
632         assert(con);
633         return con->version;
634 }
635
636 const char *
637 dhcpcd_cffile(DHCPCD_CONNECTION *con)
638 {
639
640         assert(con);
641         return con->cffile;
642 }
643
644 void
645 dhcpcd_set_if_callback(DHCPCD_CONNECTION *con,
646     void (*cb)(DHCPCD_IF *, void *), void *ctx)
647 {
648
649         assert(con);
650         con->if_cb = cb;
651         con->if_context = ctx;
652 }
653
654 void
655 dhcpcd_set_status_callback(DHCPCD_CONNECTION *con,
656     void (*cb)(DHCPCD_CONNECTION *, const char *, void *), void *ctx)
657 {
658
659         assert(con);
660         con->status_cb = cb;
661         con->status_context = ctx;
662 }
663
664 void
665 dhcpcd_close(DHCPCD_CONNECTION *con)
666 {
667         DHCPCD_WPA *wpa;
668
669         assert(con);
670
671         /* Shut down WPA listeners as they aren't much good without dhcpcd.
672          * They'll be restarted anyway when dhcpcd comes back up. */
673         for (wpa = con->wpa; wpa; wpa = wpa->next)
674                 dhcpcd_wpa_close(con->wpa);
675
676         if (con->command_fd != -1) {
677                 shutdown(con->command_fd, SHUT_RDWR);
678                 con->command_fd = -1;
679         }
680         if (con->listen_fd != -1) {
681                 shutdown(con->listen_fd, SHUT_RDWR);
682                 con->listen_fd = -1;
683         }
684
685         if (con->cffile) {
686                 free(con->cffile);
687                 con->cffile = NULL;
688         }
689         if (con->version) {
690                 free(con->version);
691                 con->version = NULL;
692         }
693         if (con->buf) {
694                 free(con->buf);
695                 con->buf = NULL;
696                 con->buflen = 0;
697         }
698 }
699
700 DHCPCD_CONNECTION *
701 dhcpcd_if_connection(DHCPCD_IF *i)
702 {
703
704         assert(i);
705         return i->con;
706 }
707
708 char *
709 dhcpcd_if_message(const DHCPCD_IF *i)
710 {
711         const char *ip, *iplen, *pfx;
712         char *msg, *p;
713         const char *reason = NULL;
714         size_t len;
715         bool showssid;
716
717         assert(i);
718         /* Don't report non SLAAC configurations */
719         if (strcmp(i->type, "ra") == 0 && i->up &&
720             dhcpcd_get_value(i, "ra1_prefix=") == NULL)
721                 return NULL;
722
723         showssid = false;
724         if (strcmp(i->reason, "EXPIRE") == 0)
725                 reason = _("Expired");
726         else if (strcmp(i->reason, "CARRIER") == 0) {
727                 if (i->wireless) {
728                         showssid = true;
729                         reason = _("Associated with");
730                 } else
731                         reason = _("Cable plugged in");
732         } else if (strcmp(i->reason, "NOCARRIER") == 0) {
733                 if (i->wireless) {
734                         if (i->ssid) {
735                                 reason = _("Disassociated from");
736                                 showssid = true;
737                         } else
738                                 reason = _("Not associated");
739                 } else
740                         reason = _("Cable unplugged");
741         } else if (strcmp(i->reason, "UNKNOWN") == 0)
742                 reason = _("Unknown link state");
743         else if (strcmp(i->reason, "FAIL") == 0)
744                 reason = _("Automatic configuration not possible");
745         else if (strcmp(i->reason, "3RDPARTY") == 0)
746                 reason = _("Waiting for 3rd Party configuration");
747
748         if (reason == NULL) {
749                 if (i->up)
750                         reason = _("Configured");
751                 else if (strcmp(i->type, "ra") == 0)
752                         reason = "Expired RA";
753                 else
754                         reason = i->reason;
755         }
756
757         pfx = i->up ? "new_" : "old_";
758         if ((ip = dhcpcd_get_prefix_value(i, pfx, "ip_address=")))
759                 iplen = dhcpcd_get_prefix_value(i, pfx, "subnet_cidr=");
760         else if ((ip = dhcpcd_get_value(i, "ra1_prefix=")))
761                 iplen = NULL;
762         else if ((ip = dhcpcd_get_prefix_value(i, pfx,
763             "dhcp6_ia_na1_ia_addr1=")))
764                 iplen = "128";
765         else {
766                 ip = NULL;
767                 iplen = NULL;
768         }
769
770         len = strlen(i->ifname) + strlen(reason) + 3;
771         if (showssid && i->ssid)
772                 len += strlen(i->ssid) + 1;
773         if (ip)
774                 len += strlen(ip) + 1;
775         if (iplen)
776                 len += strlen(iplen) + 1;
777         msg = p = malloc(len);
778         if (msg == NULL)
779                 return NULL;
780         p += snprintf(msg, len, "%s: %s", i->ifname, reason);
781         if (showssid)
782                 p += snprintf(p, len - (size_t)(p - msg), " %s", i->ssid);
783         if (iplen)
784                 p += snprintf(p, len - (size_t)(p - msg), " %s/%s", ip, iplen);
785         else if (ip)
786                 p += snprintf(p, len - (size_t)(p - msg), " %s", ip);
787         return msg;
788 }
789
790 void
791 dhcpcd_free(DHCPCD_CONNECTION *con)
792 {
793         DHCPCD_IF *nif;
794         DHCPCD_WPA *nwpa;
795
796         assert(con);
797         while (con->interfaces) {
798                 nif = con->interfaces->next;
799                 free(con->interfaces->data);
800                 free(con->interfaces);
801                 con->interfaces = nif;
802         }
803         while (con->wpa) {
804                 nwpa = con->wpa->next;
805                 dhcpcd_wpa_close(con->wpa);
806                 free(con->wpa);
807                 con->wpa = nwpa;
808         }
809         free(con);
810 }