1 /*
2 * virdnsmasq.c: Helper APIs for managing dnsmasq
3 *
4 * Copyright (C) 2007-2013 Red Hat, Inc.
5 * Copyright (C) 2010 Satoru SATOH <satoru.satoh@gmail.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see
19 * <http://www.gnu.org/licenses/>.
20 *
21 * Based on iptables.c
22 */
23
24 #include <config.h>
25
26 #include <stdarg.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <signal.h>
32
33 #include "internal.h"
34 #include "datatypes.h"
35 #include "virbitmap.h"
36 #include "virdnsmasq.h"
37 #include "virutil.h"
38 #include "vircommand.h"
39 #include "viralloc.h"
40 #include "virerror.h"
41 #include "virlog.h"
42 #include "virfile.h"
43 #include "virstring.h"
44
45 #define VIR_FROM_THIS VIR_FROM_NETWORK
46
47 VIR_LOG_INIT("util.dnsmasq");
48
49 #define DNSMASQ_HOSTSFILE_SUFFIX "hostsfile"
50 #define DNSMASQ_ADDNHOSTSFILE_SUFFIX "addnhosts"
51
52 static void
dhcphostFreeContent(dnsmasqDhcpHost * host)53 dhcphostFreeContent(dnsmasqDhcpHost *host)
54 {
55 g_free(host->host);
56 }
57
58 static void
addnhostFreeContent(dnsmasqAddnHost * host)59 addnhostFreeContent(dnsmasqAddnHost *host)
60 {
61 size_t i;
62
63 for (i = 0; i < host->nhostnames; i++)
64 g_free(host->hostnames[i]);
65 g_free(host->hostnames);
66 g_free(host->ip);
67 }
68
69 static void
addnhostsFree(dnsmasqAddnHostsfile * addnhostsfile)70 addnhostsFree(dnsmasqAddnHostsfile *addnhostsfile)
71 {
72 size_t i;
73
74 if (addnhostsfile->hosts) {
75 for (i = 0; i < addnhostsfile->nhosts; i++)
76 addnhostFreeContent(&addnhostsfile->hosts[i]);
77
78 g_free(addnhostsfile->hosts);
79
80 addnhostsfile->nhosts = 0;
81 }
82
83 g_free(addnhostsfile->path);
84
85 g_free(addnhostsfile);
86 }
87
88 static int
addnhostsAdd(dnsmasqAddnHostsfile * addnhostsfile,virSocketAddr * ip,const char * name)89 addnhostsAdd(dnsmasqAddnHostsfile *addnhostsfile,
90 virSocketAddr *ip,
91 const char *name)
92 {
93 char *ipstr = NULL;
94 int idx = -1;
95 size_t i;
96
97 if (!(ipstr = virSocketAddrFormat(ip)))
98 return -1;
99
100 for (i = 0; i < addnhostsfile->nhosts; i++) {
101 if (STREQ((const char *)addnhostsfile->hosts[i].ip, (const char *)ipstr)) {
102 idx = i;
103 break;
104 }
105 }
106
107 if (idx < 0) {
108 VIR_REALLOC_N(addnhostsfile->hosts, addnhostsfile->nhosts + 1);
109
110 idx = addnhostsfile->nhosts;
111 addnhostsfile->hosts[idx].hostnames = g_new0(char *, 1);
112
113 addnhostsfile->hosts[idx].ip = g_strdup(ipstr);
114
115 addnhostsfile->hosts[idx].nhostnames = 0;
116 addnhostsfile->nhosts++;
117 }
118
119 VIR_REALLOC_N(addnhostsfile->hosts[idx].hostnames, addnhostsfile->hosts[idx].nhostnames + 1);
120
121 addnhostsfile->hosts[idx].hostnames[addnhostsfile->hosts[idx].nhostnames] = g_strdup(name);
122
123 VIR_FREE(ipstr);
124
125 addnhostsfile->hosts[idx].nhostnames++;
126
127 return 0;
128 }
129
130 static dnsmasqAddnHostsfile *
addnhostsNew(const char * name,const char * config_dir)131 addnhostsNew(const char *name,
132 const char *config_dir)
133 {
134 dnsmasqAddnHostsfile *addnhostsfile;
135 g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
136
137 addnhostsfile = g_new0(dnsmasqAddnHostsfile, 1);
138
139 addnhostsfile->hosts = NULL;
140 addnhostsfile->nhosts = 0;
141
142 virBufferAsprintf(&buf, "%s", config_dir);
143 virBufferEscapeString(&buf, "/%s", name);
144 virBufferAsprintf(&buf, ".%s", DNSMASQ_ADDNHOSTSFILE_SUFFIX);
145
146 if (!(addnhostsfile->path = virBufferContentAndReset(&buf)))
147 goto error;
148
149 return addnhostsfile;
150
151 error:
152 addnhostsFree(addnhostsfile);
153 return NULL;
154 }
155
156 static int
addnhostsWrite(const char * path,dnsmasqAddnHost * hosts,unsigned int nhosts)157 addnhostsWrite(const char *path,
158 dnsmasqAddnHost *hosts,
159 unsigned int nhosts)
160 {
161 g_autofree char *tmp = NULL;
162 FILE *f;
163 bool istmp = true;
164 size_t i, j;
165 int rc = 0;
166
167 /* even if there are 0 hosts, create a 0 length file, to allow
168 * for runtime addition.
169 */
170
171 tmp = g_strdup_printf("%s.new", path);
172
173 if (!(f = fopen(tmp, "w"))) {
174 istmp = false;
175 if (!(f = fopen(path, "w")))
176 return -errno;
177 }
178
179 for (i = 0; i < nhosts; i++) {
180 if (fputs(hosts[i].ip, f) == EOF || fputc('\t', f) == EOF) {
181 rc = -errno;
182 VIR_FORCE_FCLOSE(f);
183
184 if (istmp)
185 unlink(tmp);
186
187 return rc;
188 }
189
190 for (j = 0; j < hosts[i].nhostnames; j++) {
191 if (fputs(hosts[i].hostnames[j], f) == EOF || fputc('\t', f) == EOF) {
192 rc = -errno;
193 VIR_FORCE_FCLOSE(f);
194
195 if (istmp)
196 unlink(tmp);
197
198 return rc;
199 }
200 }
201
202 if (fputc('\n', f) == EOF) {
203 rc = -errno;
204 VIR_FORCE_FCLOSE(f);
205
206 if (istmp)
207 unlink(tmp);
208
209 return rc;
210 }
211 }
212
213 if (VIR_FCLOSE(f) == EOF)
214 return -errno;
215
216 if (istmp && rename(tmp, path) < 0) {
217 rc = -errno;
218 unlink(tmp);
219 return rc;
220 }
221
222 return 0;
223 }
224
225 static int
addnhostsSave(dnsmasqAddnHostsfile * addnhostsfile)226 addnhostsSave(dnsmasqAddnHostsfile *addnhostsfile)
227 {
228 int err = addnhostsWrite(addnhostsfile->path, addnhostsfile->hosts,
229 addnhostsfile->nhosts);
230
231 if (err < 0) {
232 virReportSystemError(-err, _("cannot write config file '%s'"),
233 addnhostsfile->path);
234 return -1;
235 }
236
237 return 0;
238 }
239
240 static int
genericFileDelete(char * path)241 genericFileDelete(char *path)
242 {
243 if (!virFileExists(path))
244 return 0;
245
246 if (unlink(path) < 0) {
247 virReportSystemError(errno, _("cannot remove config file '%s'"),
248 path);
249 return -1;
250 }
251
252 return 0;
253 }
254
255 static void
hostsfileFree(dnsmasqHostsfile * hostsfile)256 hostsfileFree(dnsmasqHostsfile *hostsfile)
257 {
258 size_t i;
259
260 if (hostsfile->hosts) {
261 for (i = 0; i < hostsfile->nhosts; i++)
262 dhcphostFreeContent(&hostsfile->hosts[i]);
263
264 g_free(hostsfile->hosts);
265
266 hostsfile->nhosts = 0;
267 }
268
269 g_free(hostsfile->path);
270
271 g_free(hostsfile);
272 }
273
274 /* Note: There are many additional dhcp-host specifications
275 * supported by dnsmasq. There are only the basic ones.
276 */
277 static int
hostsfileAdd(dnsmasqHostsfile * hostsfile,const char * mac,virSocketAddr * ip,const char * name,const char * id,const char * leasetime,bool ipv6)278 hostsfileAdd(dnsmasqHostsfile *hostsfile,
279 const char *mac,
280 virSocketAddr *ip,
281 const char *name,
282 const char *id,
283 const char *leasetime,
284 bool ipv6)
285 {
286 g_autofree char *ipstr = NULL;
287 g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
288
289 VIR_REALLOC_N(hostsfile->hosts, hostsfile->nhosts + 1);
290
291 if (!(ipstr = virSocketAddrFormat(ip)))
292 return -1;
293
294 /* the first test determines if it is a dhcpv6 host */
295 if (ipv6) {
296 if (name && id) {
297 virBufferAsprintf(&buf, "id:%s,%s", id, name);
298 } else if (name && !id) {
299 virBufferAsprintf(&buf, "%s", name);
300 } else if (!name && id) {
301 virBufferAsprintf(&buf, "id:%s", id);
302 }
303 virBufferAsprintf(&buf, ",[%s]", ipstr);
304 } else if (name && mac) {
305 virBufferAsprintf(&buf, "%s,%s,%s", mac, ipstr, name);
306 } else if (name && !mac) {
307 virBufferAsprintf(&buf, "%s,%s", name, ipstr);
308 } else {
309 virBufferAsprintf(&buf, "%s,%s", mac, ipstr);
310 }
311
312 if (leasetime)
313 virBufferAsprintf(&buf, ",%s", leasetime);
314
315 if (!(hostsfile->hosts[hostsfile->nhosts].host = virBufferContentAndReset(&buf)))
316 return -1;
317
318 hostsfile->nhosts++;
319
320 return 0;
321 }
322
323 static dnsmasqHostsfile *
hostsfileNew(const char * name,const char * config_dir)324 hostsfileNew(const char *name,
325 const char *config_dir)
326 {
327 dnsmasqHostsfile *hostsfile;
328 g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
329
330 hostsfile = g_new0(dnsmasqHostsfile, 1);
331
332 hostsfile->hosts = NULL;
333 hostsfile->nhosts = 0;
334
335 virBufferAsprintf(&buf, "%s", config_dir);
336 virBufferEscapeString(&buf, "/%s", name);
337 virBufferAsprintf(&buf, ".%s", DNSMASQ_HOSTSFILE_SUFFIX);
338
339 if (!(hostsfile->path = virBufferContentAndReset(&buf)))
340 goto error;
341 return hostsfile;
342
343 error:
344 hostsfileFree(hostsfile);
345 return NULL;
346 }
347
348 static int
hostsfileWrite(const char * path,dnsmasqDhcpHost * hosts,unsigned int nhosts)349 hostsfileWrite(const char *path,
350 dnsmasqDhcpHost *hosts,
351 unsigned int nhosts)
352 {
353 g_autofree char *tmp = NULL;
354 FILE *f;
355 bool istmp = true;
356 size_t i;
357 int rc = 0;
358
359 /* even if there are 0 hosts, create a 0 length file, to allow
360 * for runtime addition.
361 */
362
363 tmp = g_strdup_printf("%s.new", path);
364
365 if (!(f = fopen(tmp, "w"))) {
366 istmp = false;
367 if (!(f = fopen(path, "w")))
368 return -errno;
369 }
370
371 for (i = 0; i < nhosts; i++) {
372 if (fputs(hosts[i].host, f) == EOF || fputc('\n', f) == EOF) {
373 rc = -errno;
374 VIR_FORCE_FCLOSE(f);
375
376 if (istmp)
377 unlink(tmp);
378
379 return rc;
380 }
381 }
382
383 if (VIR_FCLOSE(f) == EOF)
384 return -errno;
385
386 if (istmp && rename(tmp, path) < 0) {
387 rc = -errno;
388 unlink(tmp);
389 return rc;
390 }
391
392 return 0;
393 }
394
395 static int
hostsfileSave(dnsmasqHostsfile * hostsfile)396 hostsfileSave(dnsmasqHostsfile *hostsfile)
397 {
398 int err = hostsfileWrite(hostsfile->path, hostsfile->hosts,
399 hostsfile->nhosts);
400
401 if (err < 0) {
402 virReportSystemError(-err, _("cannot write config file '%s'"),
403 hostsfile->path);
404 return -1;
405 }
406
407 return 0;
408 }
409
410 /**
411 * dnsmasqContextNew:
412 *
413 * Create a new Dnsmasq context
414 *
415 * Returns a pointer to the new structure or NULL in case of error
416 */
417 dnsmasqContext *
dnsmasqContextNew(const char * network_name,const char * config_dir)418 dnsmasqContextNew(const char *network_name,
419 const char *config_dir)
420 {
421 dnsmasqContext *ctx;
422
423 ctx = g_new0(dnsmasqContext, 1);
424
425 ctx->config_dir = g_strdup(config_dir);
426
427 if (!(ctx->hostsfile = hostsfileNew(network_name, config_dir)))
428 goto error;
429 if (!(ctx->addnhostsfile = addnhostsNew(network_name, config_dir)))
430 goto error;
431
432 return ctx;
433
434 error:
435 dnsmasqContextFree(ctx);
436 return NULL;
437 }
438
439 /**
440 * dnsmasqContextFree:
441 * @ctx: pointer to the dnsmasq context
442 *
443 * Free the resources associated with a dnsmasq context
444 */
445 void
dnsmasqContextFree(dnsmasqContext * ctx)446 dnsmasqContextFree(dnsmasqContext *ctx)
447 {
448 if (!ctx)
449 return;
450
451 g_free(ctx->config_dir);
452
453 if (ctx->hostsfile)
454 hostsfileFree(ctx->hostsfile);
455 if (ctx->addnhostsfile)
456 addnhostsFree(ctx->addnhostsfile);
457
458 g_free(ctx);
459 }
460
461 /**
462 * dnsmasqAddDhcpHost:
463 * @ctx: pointer to the dnsmasq context for each network
464 * @mac: pointer to the string contains mac address of the host
465 * @ip: pointer to the socket address contains ip of the host
466 * @name: pointer to the string contains hostname of the host or NULL
467 *
468 * Add dhcp-host entry.
469 */
470 int
dnsmasqAddDhcpHost(dnsmasqContext * ctx,const char * mac,virSocketAddr * ip,const char * name,const char * id,const char * leasetime,bool ipv6)471 dnsmasqAddDhcpHost(dnsmasqContext *ctx,
472 const char *mac,
473 virSocketAddr *ip,
474 const char *name,
475 const char *id,
476 const char *leasetime,
477 bool ipv6)
478 {
479 return hostsfileAdd(ctx->hostsfile, mac, ip, name, id, leasetime, ipv6);
480 }
481
482 /*
483 * dnsmasqAddHost:
484 * @ctx: pointer to the dnsmasq context for each network
485 * @ip: pointer to the socket address contains ip of the host
486 * @name: pointer to the string contains hostname of the host
487 *
488 * Add additional host entry.
489 */
490
491 int
dnsmasqAddHost(dnsmasqContext * ctx,virSocketAddr * ip,const char * name)492 dnsmasqAddHost(dnsmasqContext *ctx,
493 virSocketAddr *ip,
494 const char *name)
495 {
496 return addnhostsAdd(ctx->addnhostsfile, ip, name);
497 }
498
499 /**
500 * dnsmasqSave:
501 * @ctx: pointer to the dnsmasq context for each network
502 *
503 * Saves all the configurations associated with a context to disk.
504 */
505 int
dnsmasqSave(const dnsmasqContext * ctx)506 dnsmasqSave(const dnsmasqContext *ctx)
507 {
508 int ret = 0;
509
510 if (g_mkdir_with_parents(ctx->config_dir, 0777) < 0) {
511 virReportSystemError(errno, _("cannot create config directory '%s'"),
512 ctx->config_dir);
513 return -1;
514 }
515
516 if (ctx->hostsfile)
517 ret = hostsfileSave(ctx->hostsfile);
518 if (ret == 0) {
519 if (ctx->addnhostsfile)
520 ret = addnhostsSave(ctx->addnhostsfile);
521 }
522
523 return ret;
524 }
525
526
527 /**
528 * dnsmasqDelete:
529 * @ctx: pointer to the dnsmasq context for each network
530 *
531 * Delete all the configuration files associated with a context.
532 */
533 int
dnsmasqDelete(const dnsmasqContext * ctx)534 dnsmasqDelete(const dnsmasqContext *ctx)
535 {
536 int ret = 0;
537
538 if (ctx->hostsfile)
539 ret = genericFileDelete(ctx->hostsfile->path);
540 if (ctx->addnhostsfile)
541 ret = genericFileDelete(ctx->addnhostsfile->path);
542
543 return ret;
544 }
545
546 /**
547 * dnsmasqReload:
548 * @pid: the pid of the target dnsmasq process
549 *
550 * Reloads all the configurations associated to a context
551 */
552 int
dnsmasqReload(pid_t pid G_GNUC_UNUSED)553 dnsmasqReload(pid_t pid G_GNUC_UNUSED)
554 {
555 #ifndef WIN32
556 if (kill(pid, SIGHUP) != 0) {
557 virReportSystemError(errno,
558 _("Failed to make dnsmasq (PID: %d)"
559 " reload config files."),
560 pid);
561 return -1;
562 }
563 #endif /* WIN32 */
564
565 return 0;
566 }
567
568 /*
569 * dnsmasqCapabilities functions - provide useful information about the
570 * version of dnsmasq on this machine.
571 *
572 */
573 struct _dnsmasqCaps {
574 virObject parent;
575 char *binaryPath;
576 bool noRefresh;
577 time_t mtime;
578 virBitmap *flags;
579 unsigned long version;
580 };
581
582 static virClass *dnsmasqCapsClass;
583
584 static void
dnsmasqCapsDispose(void * obj)585 dnsmasqCapsDispose(void *obj)
586 {
587 dnsmasqCaps *caps = obj;
588
589 virBitmapFree(caps->flags);
590 g_free(caps->binaryPath);
591 }
592
dnsmasqCapsOnceInit(void)593 static int dnsmasqCapsOnceInit(void)
594 {
595 if (!VIR_CLASS_NEW(dnsmasqCaps, virClassForObject()))
596 return -1;
597
598 return 0;
599 }
600
601 VIR_ONCE_GLOBAL_INIT(dnsmasqCaps);
602
603 static void
dnsmasqCapsSet(dnsmasqCaps * caps,dnsmasqCapsFlags flag)604 dnsmasqCapsSet(dnsmasqCaps *caps,
605 dnsmasqCapsFlags flag)
606 {
607 ignore_value(virBitmapSetBit(caps->flags, flag));
608 }
609
610
611 #define DNSMASQ_VERSION_STR "Dnsmasq version "
612
613 static int
dnsmasqCapsSetFromBuffer(dnsmasqCaps * caps,const char * buf)614 dnsmasqCapsSetFromBuffer(dnsmasqCaps *caps, const char *buf)
615 {
616 int len;
617 const char *p;
618
619 caps->noRefresh = true;
620
621 p = STRSKIP(buf, DNSMASQ_VERSION_STR);
622 if (!p)
623 goto fail;
624
625 virSkipToDigit(&p);
626
627 if (virParseVersionString(p, &caps->version, true) < 0)
628 goto fail;
629
630 if (strstr(buf, "--bind-dynamic"))
631 dnsmasqCapsSet(caps, DNSMASQ_CAPS_BIND_DYNAMIC);
632
633 /* if this string is a part of the --version output, dnsmasq
634 * has been patched to use SO_BINDTODEVICE when listening,
635 * so that it will only accept requests that arrived on the
636 * listening interface(s)
637 */
638 if (strstr(buf, "--bind-interfaces with SO_BINDTODEVICE"))
639 dnsmasqCapsSet(caps, DNSMASQ_CAPS_BINDTODEVICE);
640
641 if (strstr(buf, "--ra-param"))
642 dnsmasqCapsSet(caps, DNSMASQ_CAPS_RA_PARAM);
643
644 VIR_INFO("dnsmasq version is %d.%d, --bind-dynamic is %spresent, "
645 "SO_BINDTODEVICE is %sin use, --ra-param is %spresent",
646 (int)caps->version / 1000000,
647 (int)(caps->version % 1000000) / 1000,
648 dnsmasqCapsGet(caps, DNSMASQ_CAPS_BIND_DYNAMIC) ? "" : "NOT ",
649 dnsmasqCapsGet(caps, DNSMASQ_CAPS_BINDTODEVICE) ? "" : "NOT ",
650 dnsmasqCapsGet(caps, DNSMASQ_CAPS_RA_PARAM) ? "" : "NOT ");
651 return 0;
652
653 fail:
654 p = strchr(buf, '\n');
655 if (!p)
656 len = strlen(buf);
657 else
658 len = p - buf;
659 virReportError(VIR_ERR_INTERNAL_ERROR,
660 _("cannot parse %s version number in '%.*s'"),
661 caps->binaryPath, len, buf);
662 return -1;
663
664 }
665
666 static int
dnsmasqCapsRefreshInternal(dnsmasqCaps * caps,bool force)667 dnsmasqCapsRefreshInternal(dnsmasqCaps *caps, bool force)
668 {
669 int ret = -1;
670 struct stat sb;
671 virCommand *cmd = NULL;
672 g_autofree char *help = NULL;
673 g_autofree char *version = NULL;
674 g_autofree char *complete = NULL;
675
676 if (!caps || caps->noRefresh)
677 return 0;
678
679 if (stat(caps->binaryPath, &sb) < 0) {
680 virReportSystemError(errno, _("Cannot check dnsmasq binary %s"),
681 caps->binaryPath);
682 return -1;
683 }
684 if (!force && caps->mtime == sb.st_mtime)
685 return 0;
686 caps->mtime = sb.st_mtime;
687
688 /* Make sure the binary we are about to try exec'ing exists.
689 * Technically we could catch the exec() failure, but that's
690 * in a sub-process so it's hard to feed back a useful error.
691 */
692 if (!virFileIsExecutable(caps->binaryPath)) {
693 virReportSystemError(errno, _("dnsmasq binary %s is not executable"),
694 caps->binaryPath);
695 goto cleanup;
696 }
697
698 cmd = virCommandNewArgList(caps->binaryPath, "--version", NULL);
699 virCommandSetOutputBuffer(cmd, &version);
700 virCommandAddEnvPassCommon(cmd);
701 virCommandClearCaps(cmd);
702 if (virCommandRun(cmd, NULL) < 0)
703 goto cleanup;
704 virCommandFree(cmd);
705
706 cmd = virCommandNewArgList(caps->binaryPath, "--help", NULL);
707 virCommandSetOutputBuffer(cmd, &help);
708 virCommandAddEnvPassCommon(cmd);
709 virCommandClearCaps(cmd);
710 if (virCommandRun(cmd, NULL) < 0)
711 goto cleanup;
712
713 complete = g_strdup_printf("%s\n%s", version, help);
714
715 ret = dnsmasqCapsSetFromBuffer(caps, complete);
716
717 cleanup:
718 virCommandFree(cmd);
719 return ret;
720 }
721
722 static dnsmasqCaps *
dnsmasqCapsNewEmpty(const char * binaryPath)723 dnsmasqCapsNewEmpty(const char *binaryPath)
724 {
725 dnsmasqCaps *caps;
726
727 if (dnsmasqCapsInitialize() < 0)
728 return NULL;
729 if (!(caps = virObjectNew(dnsmasqCapsClass)))
730 return NULL;
731 caps->flags = virBitmapNew(DNSMASQ_CAPS_LAST);
732 caps->binaryPath = g_strdup(binaryPath ? binaryPath : DNSMASQ);
733 return caps;
734 }
735
736 dnsmasqCaps *
dnsmasqCapsNewFromBuffer(const char * buf)737 dnsmasqCapsNewFromBuffer(const char *buf)
738 {
739 dnsmasqCaps *caps = dnsmasqCapsNewEmpty(DNSMASQ);
740
741 if (!caps)
742 return NULL;
743
744 if (dnsmasqCapsSetFromBuffer(caps, buf) < 0) {
745 virObjectUnref(caps);
746 return NULL;
747 }
748 return caps;
749 }
750
751 dnsmasqCaps *
dnsmasqCapsNewFromBinary(void)752 dnsmasqCapsNewFromBinary(void)
753 {
754 dnsmasqCaps *caps = dnsmasqCapsNewEmpty(DNSMASQ);
755
756 if (!caps)
757 return NULL;
758
759 if (dnsmasqCapsRefreshInternal(caps, true) < 0) {
760 virObjectUnref(caps);
761 return NULL;
762 }
763 return caps;
764 }
765
766 const char *
dnsmasqCapsGetBinaryPath(dnsmasqCaps * caps)767 dnsmasqCapsGetBinaryPath(dnsmasqCaps *caps)
768 {
769 return caps ? caps->binaryPath : DNSMASQ;
770 }
771
772 unsigned long
dnsmasqCapsGetVersion(dnsmasqCaps * caps)773 dnsmasqCapsGetVersion(dnsmasqCaps *caps)
774 {
775 if (caps)
776 return caps->version;
777 else
778 return 0;
779 }
780
781 bool
dnsmasqCapsGet(dnsmasqCaps * caps,dnsmasqCapsFlags flag)782 dnsmasqCapsGet(dnsmasqCaps *caps, dnsmasqCapsFlags flag)
783 {
784 return caps && virBitmapIsBitSet(caps->flags, flag);
785 }
786
787
788 /** dnsmasqDhcpHostsToString:
789 *
790 * Turns a vector of dnsmasqDhcpHost into the string that is ought to be
791 * stored in the hostsfile, this functionality is split to make hostsfiles
792 * testable. Returns NULL if nhosts is 0.
793 */
794 char *
dnsmasqDhcpHostsToString(dnsmasqDhcpHost * hosts,unsigned int nhosts)795 dnsmasqDhcpHostsToString(dnsmasqDhcpHost *hosts,
796 unsigned int nhosts)
797 {
798 size_t i;
799 g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
800
801 for (i = 0; i < nhosts; i++)
802 virBufferAsprintf(&buf, "%s\n", hosts[i].host);
803
804 return virBufferContentAndReset(&buf);
805 }
806