Parse config options with ragel and support a configuration file.

This commit is contained in:
Nicholas J. Kain 2014-04-14 15:06:31 -04:00
parent 51033d3664
commit a501789e04
7 changed files with 386 additions and 179 deletions

View File

@ -5,6 +5,7 @@ cmake_minimum_required (VERSION 2.6)
include_directories("${PROJECT_SOURCE_DIR}")
set(RAGEL_IFCHD_PARSE ${CMAKE_CURRENT_BINARY_DIR}/ifchd-parse.c)
set(RAGEL_CFG_PARSE ${CMAKE_CURRENT_BINARY_DIR}/cfg.c)
find_program(RAGEL ragel)
add_custom_command(
@ -15,8 +16,16 @@ add_custom_command(
COMMENT "Compiling Ragel state machine: ifchd-parse.rl"
VERBATIM
)
add_custom_command(
OUTPUT ${RAGEL_CFG_PARSE}
COMMAND ${RAGEL} -G2 -o ${RAGEL_CFG_PARSE} cfg.rl
DEPENDS cfg.rl
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Compiling Ragel state machine: cfg.rl"
VERBATIM
)
file(GLOB NDHC_SRCS "*.c")
add_executable(ndhc ${RAGEL_IFCHD_PARSE} ${NDHC_SRCS})
add_executable(ndhc ${RAGEL_CFG_PARSE} ${RAGEL_IFCHD_PARSE} ${NDHC_SRCS})
target_link_libraries(ndhc ncmlib)

View File

@ -122,7 +122,7 @@ static struct arp_data garp = {
.server_replied = false,
};
void set_arp_relentless_def(void) { garp.relentless_def = true; }
void set_arp_relentless_def(bool v) { garp.relentless_def = v; }
static void arp_reply_clear(void)
{

View File

@ -28,6 +28,7 @@
#ifndef ARP_H_
#define ARP_H_
#include <stdbool.h>
#include <stdint.h>
#include <net/if_arp.h>
#include "ndhc.h"
@ -57,7 +58,7 @@ extern int arp_probe_num;
extern int arp_probe_min;
extern int arp_probe_max;
void set_arp_relentless_def(void);
void set_arp_relentless_def(bool v);
void arp_reset_send_stats(void);
void arp_close_fd(struct client_state_t *cs);
int arp_check(struct client_state_t *cs, struct dhcpmsg *packet);

7
src/cfg.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef NDHC_CONFIG_H_
#define NDHC_CONFIG_H_
void parse_cmdline(int argc, char *argv[]);
#endif /* NDHC_CONFIG_H_ */

321
src/cfg.rl Normal file
View File

@ -0,0 +1,321 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "ndhc-defines.h"
#include "cfg.h"
#include "arp.h"
#include "ndhc.h"
#include "ifchd.h"
#include "sockd.h"
#include "seccomp.h"
#include "nk/log.h"
#include "nk/privilege.h"
#include "nk/copy_cmdarg.h"
struct cfgparse {
char buf[MAX_BUF];
size_t buflen;
int ternary; // = 0 nothing, -1 = false, +1 = true
int cs;
};
%%{
machine cfg_actions;
access ccfg.;
action clear { memset(&ccfg, 0, sizeof ccfg); }
action append {
if (ccfg.buflen < sizeof ccfg.buf - 1)
ccfg.buf[ccfg.buflen++] = *p;
else
suicide("line or option is too long");
}
action term {
if (ccfg.buflen < sizeof ccfg.buf)
ccfg.buf[ccfg.buflen] = 0;
}
action truval { ccfg.ternary = 1; }
action falsval { ccfg.ternary = -1; }
action clientid { get_clientid_string(ccfg.buf, ccfg.buflen); }
action background {
switch (ccfg.ternary) {
case 1:
client_config.background_if_no_lease = 1;
gflags_detach = 1;
break;
case -1:
client_config.background_if_no_lease = 0;
gflags_detach = 0;
default:
break;
}
}
action pidfile {
copy_cmdarg(pidfile, ccfg.buf, sizeof pidfile, "pidfile");
}
action hostname {
copy_cmdarg(client_config.hostname, ccfg.buf,
sizeof client_config.hostname, "hostname");
}
action interface {
copy_cmdarg(client_config.interface, ccfg.buf,
sizeof client_config.interface, "interface");
}
action now {
switch (ccfg.ternary) {
case 1: client_config.abort_if_no_lease = 1; break;
case -1: client_config.abort_if_no_lease = 0; default: break;
}
}
action quit {
switch (ccfg.ternary) {
case 1: client_config.quit_after_lease = 1; break;
case -1: client_config.quit_after_lease = 0; default: break;
}
}
action request { set_client_addr(ccfg.buf); }
action vendorid {
copy_cmdarg(client_config.vendor, ccfg.buf,
sizeof client_config.vendor, "vendorid");
}
action user {
if (nk_uidgidbyname(ccfg.buf, &ndhc_uid, &ndhc_gid))
suicide("invalid ndhc user '%s' specified", ccfg.buf);
}
action ifch_user {
if (nk_uidgidbyname(ccfg.buf, &ifch_uid, &ifch_gid))
suicide("invalid ifch user '%s' specified", ccfg.buf);
}
action sockd_user {
if (nk_uidgidbyname(ccfg.buf, &sockd_uid, &sockd_gid))
suicide("invalid sockd user '%s' specified", ccfg.buf);
}
action chroot {
copy_cmdarg(chroot_dir, ccfg.buf, sizeof chroot_dir, "chroot");
}
action state_dir {
copy_cmdarg(state_dir, ccfg.buf, sizeof state_dir, "state-dir");
}
action seccomp_enforce {
switch (ccfg.ternary) {
case 1: seccomp_enforce = true; break;
case -1: seccomp_enforce = false; default: break;
}
}
action relentless_defense {
switch (ccfg.ternary) {
case 1: set_arp_relentless_def(true); break;
case -1: set_arp_relentless_def(false); default: break;
}
}
action arp_probe_wait {
int t = atoi(ccfg.buf);
if (t >= 0)
arp_probe_wait = t;
}
action arp_probe_num {
int t = atoi(ccfg.buf);
if (t >= 0)
arp_probe_num = t;
}
action arp_probe_min {
int t = atoi(ccfg.buf);
arp_probe_min = t;
if (arp_probe_min > arp_probe_max) {
t = arp_probe_max;
arp_probe_max = arp_probe_min;
arp_probe_min = t;
}
}
action arp_probe_max {
int t = atoi(ccfg.buf);
arp_probe_max = t;
if (arp_probe_min > arp_probe_max) {
t = arp_probe_max;
arp_probe_max = arp_probe_min;
arp_probe_min = t;
}
}
action gw_metric {
char *q;
long mt = strtol(ccfg.buf, &q, 10);
if (q == ccfg.buf)
suicide("gw-metric arg '%s' isn't a valid number", ccfg.buf);
if (mt > INT_MAX)
suicide("gw-metric arg '%s' is too large", ccfg.buf);
if (mt < 0)
mt = 0;
client_config.metric = (int)mt;
}
action resolv_conf {
copy_cmdarg(resolv_conf_d, ccfg.buf, sizeof resolv_conf_d,
"resolv-conf");
}
action dhcp_set_hostname {
switch (ccfg.ternary) {
case 1: allow_hostname = 1; break;
case -1: allow_hostname = 0; default: break;
}
}
action version { print_version(); exit(EXIT_SUCCESS); }
action help { show_usage(); exit(EXIT_SUCCESS); }
}%%
%%{
machine file_cfg;
access ccfg.;
include cfg_actions;
spc = [ \t];
delim = spc* '=' spc*;
string = [^\n]+ >clear $append %term;
term = '\n';
value = delim string term;
truval = ('true'|'1') % truval;
falsval = ('false'|'0') % falsval;
boolval = delim (truval|falsval) term;
blankline = term;
clientid = 'clientid' value @clientid;
background = 'background' value @background;
pidfile = 'pidfile' value @pidfile;
hostname = 'hostname' value @hostname;
interface = 'interface' value @interface;
now = 'now' boolval @now;
quit = 'quit' boolval @quit;
request = 'request' value @request;
vendorid = 'vendorid' value @vendorid;
user = 'user' value @user;
ifch_user = 'ifch-user' value @ifch_user;
sockd_user = 'sockd-user' value @sockd_user;
chroot = 'chroot' value @chroot;
state_dir = 'state-dir' value @state_dir;
seccomp_enforce = 'seccomp-enforce' boolval @seccomp_enforce;
relentless_defense = 'relentless-defense' boolval @relentless_defense;
arp_probe_wait = 'arp-probe-wait' value @arp_probe_wait;
arp_probe_num = 'arp-probe-num' value @arp_probe_num;
arp_probe_min = 'arp-probe-min' value @arp_probe_min;
arp_probe_max = 'arp-probe-max' value @arp_probe_max;
gw_metric = 'gw-metric' value @gw_metric;
resolv_conf = 'resolv-conf' value @resolv_conf;
dhcp_set_hostname = 'dhcp-set-hostname' boolval @dhcp_set_hostname;
main := blankline |
clientid | background | pidfile | hostname | interface | now | quit |
request | vendorid | user | ifch_user | sockd_user | chroot |
state_dir | seccomp_enforce | relentless_defense | arp_probe_wait |
arp_probe_num | arp_probe_min | arp_probe_max | gw_metric |
resolv_conf | dhcp_set_hostname
;
}%%
%% write data;
static void parse_cfgfile(const char *fname)
{
struct cfgparse ccfg;
memset(&ccfg, 0, sizeof ccfg);
FILE *f = fopen(fname, "r");
if (!f)
suicide("Unable to open config file '%s'.", fname);
char l[MAX_BUF];
size_t linenum = 0;
while (linenum++, fgets(l, sizeof l, f)) {
size_t llen = strlen(l);
const char *p = l;
const char *pe = l + llen;
%% write init;
%% write exec;
if (ccfg.cs == file_cfg_error)
suicide("error parsing config file line %zu: malformed", linenum);
if (ccfg.cs < file_cfg_first_final)
suicide("error parsing config file line %zu: incomplete", linenum);
}
fclose(f);
}
%%{
machine cmd_cfg;
access ccfg.;
include cfg_actions;
action cfgfile { parse_cfgfile(ccfg.buf); }
action tbv { ccfg.ternary = 1; }
string = [^\0]+ >clear $append %term;
argval = 0 string 0;
tbv = 0 % tbv;
cfgfile = ('-c'|'--config') argval @cfgfile;
clientid = ('-I'|'--clientid') argval @clientid;
background = ('-b'|'--background') tbv @background;
pidfile = ('-p'|'--pidfile') argval @pidfile;
hostname = ('-h'|'--hostname') argval @hostname;
interface = ('-i'|'--interface') argval @interface;
now = ('-n'|'--now') tbv @now;
quit = ('-q'|'--quit') tbv @quit;
request = ('-r'|'--request') argval @request;
vendorid = ('-V'|'--vendorid') argval @vendorid;
user = ('-u'|'--user') argval @user;
ifch_user = ('-U'|'--ifch-user') argval @ifch_user;
sockd_user = ('-D'|'--sockd-user') argval @sockd_user;
chroot = ('-C'|'--chroot') argval @chroot;
state_dir = ('-s'|'--state-dir') argval @state_dir;
seccomp_enforce = ('-S'|'--seccomp-enforce') tbv @seccomp_enforce;
relentless_defense = ('-d'|'--relentless-defense') tbv @relentless_defense;
arp_probe_wait = ('-w'|'--arp-probe-wait') argval @arp_probe_wait;
arp_probe_num = ('-W'|'--arp-probe-num') argval @arp_probe_num;
arp_probe_min = ('-m'|'--arp-probe-min') argval @arp_probe_min;
arp_probe_max = ('-M'|'--arp-probe-max') argval @arp_probe_max;
gw_metric = ('-t'|'--gw-metric') argval @gw_metric;
resolv_conf = ('-R'|'--resolv-conf') argval @resolv_conf;
dhcp_set_hostname = ('-H'|'--dhcp-set-hostname') tbv @dhcp_set_hostname;
version = ('-v'|'--version') 0 @version;
help = ('-?'|'--help') 0 @help;
main := (
cfgfile | clientid | background | pidfile | hostname | interface |
now | quit | request | vendorid | user | ifch_user | sockd_user |
chroot | state_dir | seccomp_enforce | relentless_defense |
arp_probe_wait | arp_probe_num | arp_probe_min | arp_probe_max |
gw_metric | resolv_conf | dhcp_set_hostname | version | help
)*;
}%%
%% write data;
void parse_cmdline(int argc, char *argv[])
{
char argb[8192];
size_t argbl = 0;
for (size_t i = 1; i < (size_t)argc; ++i) {
ssize_t snl;
if (i > 1)
snl = snprintf(argb + argbl, sizeof argb - argbl, "%c%s",
0, argv[i]);
else
snl = snprintf(argb + argbl, sizeof argb - argbl, "%s", argv[i]);
if (snl < 0 || (size_t)snl >= sizeof argb)
suicide("error parsing command line option: option too long");
argbl += snl;
}
struct cfgparse ccfg;
memset(&ccfg, 0, sizeof ccfg);
const char *p = argb;
const char *pe = argb + argbl + 1;
const char *eof = pe;
%% write init;
%% write exec;
if (ccfg.cs == cmd_cfg_error)
suicide("error parsing command line option: malformed");
if (ccfg.cs >= cmd_cfg_first_final)
return;
suicide("error parsing command line option: incomplete");
}

View File

@ -32,7 +32,6 @@
#include <sys/types.h>
#include <sys/file.h>
#include <unistd.h>
#include <getopt.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
@ -57,6 +56,7 @@
#include "ndhc.h"
#include "ndhc-defines.h"
#include "cfg.h"
#include "seccomp.h"
#include "state.h"
#include "options.h"
@ -94,13 +94,42 @@ struct client_config_t client_config = {
.foreground = 1,
};
static void show_usage(void)
void set_client_addr(const char *v) { cs.clientAddr = inet_addr(v); }
void print_version(void)
{
printf("ndhc %s, dhcp client.\n", NDHC_VERSION);
printf("Copyright (c) 2004-2014 Nicholas J. Kain\n"
"All rights reserved.\n\n"
"Redistribution and use in source and binary forms, with or without\n"
"modification, are permitted provided that the following conditions are met:\n\n"
"- Redistributions of source code must retain the above copyright notice,\n"
" this list of conditions and the following disclaimer.\n"
"- Redistributions in binary form must reproduce the above copyright notice,\n"
" this list of conditions and the following disclaimer in the documentation\n"
" and/or other materials provided with the distribution.\n\n"
"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n"
"AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n"
"IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
"ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\n"
"LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n"
"CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n"
"SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n"
"INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n"
"CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n"
"ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n"
"POSSIBILITY OF SUCH DAMAGE.\n");
exit(EXIT_SUCCESS);
}
void show_usage(void)
{
printf(
"ndhc " NDHC_VERSION ", dhcp client. Licensed under 2-clause BSD.\n"
"Copyright (C) 2004-2014 Nicholas J. Kain\n"
"Usage: ndhc [OPTIONS]\n\n"
" -c, --clientid=CLIENTID Client identifier\n"
" -c, --config=FILE Path to ndhc configuration file\n"
" -I, --clientid=CLIENTID Client identifier\n"
" -h, --hostname=HOSTNAME Client hostname\n"
" -V, --vendorid=VENDORID Client vendor identification string\n"
" -b, --background Fork to background if lease cannot be\n"
@ -127,7 +156,7 @@ static void show_usage(void)
" -M, --arp-probe-max Max ms to wait for ARP response\n"
" -t, --gw-metric Route metric for default gw (default: 0)\n"
" -R, --resolve-conf=FILE Path to resolv.conf or equivalent\n"
" -H, --dhcp-hostname Allow DHCP to set machine hostname\n"
" -H, --dhcp-set-hostname Allow DHCP to set machine hostname\n"
" -v, --version Display version\n"
);
exit(EXIT_SUCCESS);
@ -205,7 +234,7 @@ static int is_string_hwaddr(char *str, size_t slen)
return 0;
}
static int get_clientid_string(char *str, size_t slen)
int get_clientid_string(char *str, size_t slen)
{
if (!slen)
return -1;
@ -333,9 +362,9 @@ jumpstart:
char state_dir[PATH_MAX] = "/etc/ndhc";
char chroot_dir[PATH_MAX] = "";
char resolv_conf_d[PATH_MAX] = "";
static char pidfile[PATH_MAX] = PID_FILE_DEFAULT;
static uid_t ndhc_uid = 0;
static gid_t ndhc_gid = 0;
char pidfile[PATH_MAX] = PID_FILE_DEFAULT;
uid_t ndhc_uid = 0;
gid_t ndhc_gid = 0;
int ifchSock[2];
int ifchPipe[2];
int sockdSock[2];
@ -432,176 +461,9 @@ void background(void)
write_pid(pidfile);
}
static void parse_program_options(int argc, char *argv[])
{
static const struct option arg_options[] = {
{"clientid", required_argument, 0, 'c'},
{"background", no_argument, 0, 'b'},
{"pidfile", required_argument, 0, 'p'},
{"hostname", required_argument, 0, 'h'},
{"interface", required_argument, 0, 'i'},
{"now", no_argument, 0, 'n'},
{"quit", no_argument, 0, 'q'},
{"request", required_argument, 0, 'r'},
{"vendorid", required_argument, 0, 'V'},
{"user", required_argument, 0, 'u'},
{"ifch-user", required_argument, 0, 'U'},
{"sockd-user", required_argument, 0, 'D'},
{"chroot", required_argument, 0, 'C'},
{"state-dir", required_argument, 0, 's'},
{"seccomp-enforce", no_argument, 0, 'S'},
{"relentless-defense", no_argument, 0, 'd'},
{"arp-probe-wait", required_argument, 0, 'w'},
{"arp-probe-num", required_argument, 0, 'W'},
{"arp-probe-min", required_argument, 0, 'm'},
{"arp-probe-max", required_argument, 0, 'M'},
{"gw-metric", required_argument, 0, 't'},
{"resolv-conf", required_argument, 0, 'R'},
{"dhcp-set-hostname", no_argument, 0, 'H'},
{"version", no_argument, 0, 'v'},
{"help", no_argument, 0, '?'},
{0, 0, 0, 0}
};
while (1) {
int c;
c = getopt_long(argc, argv, "c:bp:P:h:i:nqr:V:u:U:D:C:s:Sdw:W:m:M:t:R:Hv?",
arg_options, NULL);
if (c < 0) break;
switch (c) {
case 'c':
get_clientid_string(optarg, strlen(optarg));
break;
case 'b':
client_config.background_if_no_lease = 1;
gflags_detach = 1;
break;
case 'p':
copy_cmdarg(pidfile, optarg, sizeof pidfile, "pidfile");
break;
case 'h':
copy_cmdarg(client_config.hostname, optarg,
sizeof client_config.hostname, "hostname");
break;
case 'i':
copy_cmdarg(client_config.interface, optarg,
sizeof client_config.interface, "interface");
break;
case 'n':
client_config.abort_if_no_lease = 1;
break;
case 'q':
client_config.quit_after_lease = 1;
break;
case 'r':
cs.clientAddr = inet_addr(optarg);
break;
case 'u':
if (nk_uidgidbyname(optarg, &ndhc_uid, &ndhc_gid))
suicide("invalid ndhc user '%s' specified", optarg);
break;
case 'U':
if (nk_uidgidbyname(optarg, &ifch_uid, &ifch_gid))
suicide("invalid ifch user '%s' specified", optarg);
break;
case 'D':
if (nk_uidgidbyname(optarg, &sockd_uid, &sockd_gid))
suicide("invalid sockd user '%s' specified", optarg);
break;
case 'C':
copy_cmdarg(chroot_dir, optarg, sizeof chroot_dir, "chroot");
break;
case 's':
copy_cmdarg(state_dir, optarg, sizeof state_dir, "state-dir");
break;
case 'S':
seccomp_enforce = true;
break;
case 'd':
set_arp_relentless_def();
break;
case 'w':
case 'W': {
int t = atoi(optarg);
if (t < 0)
break;
if (c == 'w')
arp_probe_wait = t;
else
arp_probe_num = t;
break;
}
case 'm':
case 'M': {
int t = atoi(optarg);
if (c == 'm')
arp_probe_min = t;
else
arp_probe_max = t;
if (arp_probe_min > arp_probe_max) {
t = arp_probe_max;
arp_probe_max = arp_probe_min;
arp_probe_min = t;
}
break;
}
case 'v':
printf("ndhc %s, dhcp client.\n", NDHC_VERSION);
printf("Copyright (c) 2004-2014 Nicholas J. Kain\n"
"All rights reserved.\n\n"
"Redistribution and use in source and binary forms, with or without\n"
"modification, are permitted provided that the following conditions are met:\n\n"
"- Redistributions of source code must retain the above copyright notice,\n"
" this list of conditions and the following disclaimer.\n"
"- Redistributions in binary form must reproduce the above copyright notice,\n"
" this list of conditions and the following disclaimer in the documentation\n"
" and/or other materials provided with the distribution.\n\n"
"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n"
"AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n"
"IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
"ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\n"
"LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n"
"CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n"
"SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n"
"INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n"
"CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n"
"ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n"
"POSSIBILITY OF SUCH DAMAGE.\n");
exit(EXIT_SUCCESS);
break;
case 'V':
copy_cmdarg(client_config.vendor, optarg,
sizeof client_config.vendor, "vendorid");
break;
case 't': {
char *p;
long mt = strtol(optarg, &p, 10);
if (p == optarg)
suicide("gw-metric arg '%s' isn't a valid number", optarg);
if (mt > INT_MAX)
suicide("gw-metric arg '%s' is too large", optarg);
if (mt < 0)
mt = 0;
client_config.metric = (int)mt;
break;
}
case 'R':
copy_cmdarg(resolv_conf_d, optarg, sizeof resolv_conf_d,
"resolv-conf");
break;
case 'H':
allow_hostname = 1;
break;
default:
show_usage();
}
}
}
int main(int argc, char *argv[])
{
parse_program_options(argc, argv);
parse_cmdline(argc, argv);
nk_random_u32_init(&cs.rnd32_state);

View File

@ -74,7 +74,14 @@ extern int sockdPipe[2];
extern char state_dir[PATH_MAX];
extern char chroot_dir[PATH_MAX];
extern char resolv_conf_d[PATH_MAX];
extern char pidfile[PATH_MAX];
extern uid_t ndhc_uid;
extern gid_t ndhc_gid;
void set_client_addr(const char *v);
void show_usage(void);
int get_clientid_string(char *str, size_t slen);
void background(void);
void print_version(void);
#endif /* NJK_NDHC_NDHC_H_ */