Provide a gmake Makefile for distro builds and packagers.
Define _GNU_SOURCE in the CFLAGS. Update the README. Remove the duplicate Gentoo init script ndhc.sh that is in the root. Remove DESIGN -- it's outdated.
This commit is contained in:
parent
efe50a30a6
commit
2aff36a274
@ -2,8 +2,8 @@ project (ndhcp)
|
|||||||
|
|
||||||
cmake_minimum_required (VERSION 2.6)
|
cmake_minimum_required (VERSION 2.6)
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s -std=gnu99 -pedantic -Wall -lcap -DHAVE_CLEARENV -DLINUX")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s -std=gnu99 -pedantic -Wall -lcap -D_GNU_SOURCE -DHAVE_CLEARENV -DLINUX")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -s -std=gnu99 -pedantic -Wall -lcap -DHAVE_CLEARENV -DLINUX")
|
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -s -std=gnu99 -pedantic -Wall -lcap -D_GNU_SOURCE -DHAVE_CLEARENV -DLINUX")
|
||||||
|
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/ncmlib")
|
include_directories("${PROJECT_SOURCE_DIR}/ncmlib")
|
||||||
add_subdirectory(ncmlib)
|
add_subdirectory(ncmlib)
|
||||||
|
70
DESIGN
70
DESIGN
@ -1,70 +0,0 @@
|
|||||||
Goals:
|
|
||||||
|
|
||||||
1. Security
|
|
||||||
|
|
||||||
a. Divide into seperate processes that each have the minimal
|
|
||||||
system access necessary to complete their task.
|
|
||||||
|
|
||||||
b. Use a well defined IPC mechanism to facilitate cooperation
|
|
||||||
between processes. In this case, UNIX domain sockets are
|
|
||||||
used, since they allow for UNIX DAC (on Linux, at least).
|
|
||||||
|
|
||||||
c. Write each program to be secure; don't rely on the
|
|
||||||
privilege seperations for security.
|
|
||||||
|
|
||||||
d. Simple error handling is favored rather than complex error
|
|
||||||
handling that may possibly be caused to "recover" in an
|
|
||||||
exploitable way.
|
|
||||||
|
|
||||||
e. Don't make stupid assumptions. Implement only the minimal
|
|
||||||
functionality necessary to perform a task. Expect brain
|
|
||||||
damaged or malicious inputs.
|
|
||||||
|
|
||||||
f. Run inside a chroot, with minimal privileges via
|
|
||||||
capabilities or MAC.
|
|
||||||
|
|
||||||
2. Reliability
|
|
||||||
|
|
||||||
a. Don't try to handle severe errors.
|
|
||||||
|
|
||||||
b. Log errors if program state is still sane.
|
|
||||||
|
|
||||||
c. Recover from predictable problems if necessary. Make sure
|
|
||||||
that recovery behavior is well understood and defined.
|
|
||||||
|
|
||||||
d. Complicated or unsafe recoveries should not be performed;
|
|
||||||
instead the program should promptly exit. Dead programs
|
|
||||||
don't cause exploits.
|
|
||||||
|
|
||||||
3. Portability
|
|
||||||
|
|
||||||
a. Portability is good, but portability may not be as wide as
|
|
||||||
a less secure program. Capabilities or MAC are not well
|
|
||||||
standardized, but remain necessary features.
|
|
||||||
|
|
||||||
4. Miscellaneous
|
|
||||||
|
|
||||||
a. Speed: If we aren't required to sacrifice anything more
|
|
||||||
important, it's always good to be fast.
|
|
||||||
|
|
||||||
a. Size: If we aren't required to sacrifice anything more
|
|
||||||
important, it's always good to be frugal.
|
|
||||||
|
|
||||||
Layout:
|
|
||||||
|
|
||||||
ndhc daemon (root -> chroot -> drop all !(CAP_NET_BROADCAST|CAP_NET_RAW)
|
|
||||||
-> nopriv)
|
|
||||||
|
|
||||||
* handles dhcp protocol issues, netlink hw link notifications, and ARP checks
|
|
||||||
* keeps track of leases
|
|
||||||
* talks to ifchd to perform tasks that require
|
|
||||||
higher privileges than CAP_NET_BROADCAST or CAP_NET_RAW
|
|
||||||
|
|
||||||
ifchd daemon (root -> openfd -> chroot -> drop all !CAP_NET_ADMIN -> nopriv)
|
|
||||||
|
|
||||||
* listens for interface change requests via UNIX domain socket
|
|
||||||
* restricts valid IP ranges that will be accepted
|
|
||||||
* performs interface changes
|
|
||||||
* keeps rw fds for system files (such as /etc/resolv.conf) that must
|
|
||||||
be modified outside the chroot
|
|
||||||
|
|
42
Makefile
Normal file
42
Makefile
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# This is a pretty basic makefile. I generally use CMake, so this is mostly
|
||||||
|
# for distros that want to avoid build dependencies. Produced exes will be
|
||||||
|
# at './build/ndhc' and './build/ifchd'.
|
||||||
|
|
||||||
|
NCM_SRCS = $(sort $(wildcard ncmlib/*.c))
|
||||||
|
IFCHD_SRCS = $(sort $(wildcard ifchd/*.c))
|
||||||
|
NDHC_SRCS = $(sort $(wildcard ndhc/*.c))
|
||||||
|
NCM_OBJS = $(NCM_SRCS:.c=.o)
|
||||||
|
IFCHD_OBJS = $(IFCHD_SRCS:.c=.o)
|
||||||
|
NDHC_OBJS = $(NDHC_SRCS:.c=.o)
|
||||||
|
NCM_INC = -I./ncmlib
|
||||||
|
BUILD_DIR = build
|
||||||
|
OBJ_DIR = objs
|
||||||
|
|
||||||
|
CC = gcc
|
||||||
|
AR = ar
|
||||||
|
RANLIB = ranlib
|
||||||
|
CFLAGS = -O2 -s -std=gnu99 -pedantic -Wall -D_GNU_SOURCE -DHAVE_CLEARENV -DLINUX
|
||||||
|
|
||||||
|
all: makedir ncmlib.a ifchd ndhc
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -Rf $(OBJ_DIR) $(BUILD_DIR)
|
||||||
|
|
||||||
|
makedir:
|
||||||
|
mkdir -p $(OBJ_DIR)/ndhc $(OBJ_DIR)/ifchd $(OBJ_DIR)/ncmlib $(BUILD_DIR)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) $(CFLAGS) $(NCM_INC) -c -o $(OBJ_DIR)/$@ $<
|
||||||
|
|
||||||
|
ncmlib.a: $(NCM_OBJS)
|
||||||
|
$(AR) rc $(BUILD_DIR)/$@ $(subst ncmlib/,$(OBJ_DIR)/ncmlib/,$(NCM_OBJS))
|
||||||
|
$(RANLIB) $(BUILD_DIR)/$@
|
||||||
|
|
||||||
|
ifchd: $(IFCHD_OBJS)
|
||||||
|
$(CC) $(CFLAGS) $(NCM_INC) -o $(BUILD_DIR)/$@ $(subst ifchd/,$(OBJ_DIR)/ifchd/,$(IFCHD_OBJS)) $(BUILD_DIR)/ncmlib.a -lcap
|
||||||
|
|
||||||
|
ndhc: $(NDHC_OBJS)
|
||||||
|
$(CC) $(CFLAGS) $(NCM_INC) -o $(BUILD_DIR)/$@ $(subst ndhc/,$(OBJ_DIR)/ndhc/,$(NDHC_OBJS)) $(BUILD_DIR)/ncmlib.a -lcap
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
140
README
140
README
@ -1,4 +1,4 @@
|
|||||||
ifchd, copyright (c) 2004-2011 Nicholas Kain. Licensed under GNU GPL.
|
ndhc + ifchd, copyright (c) 2004-2011 Nicholas Kain. Licensed under GNU GPL2.
|
||||||
|
|
||||||
Requirements:
|
Requirements:
|
||||||
|
|
||||||
@ -47,12 +47,19 @@ taken to prevent unintentional ARP flooding under any circumstance.
|
|||||||
|
|
||||||
ndhc also monitors hardware link status via netlink events and reacts
|
ndhc also monitors hardware link status via netlink events and reacts
|
||||||
appropriately when interface carrier status changes or an interface is
|
appropriately when interface carrier status changes or an interface is
|
||||||
explicitly deconfigured.
|
explicitly deconfigured. This functionality can be useful on wired networks
|
||||||
|
when transient carrier downtimes occur (or cables are changed), but it is
|
||||||
|
particularly useful on wireless networks.
|
||||||
|
|
||||||
USAGE
|
USAGE
|
||||||
-----
|
-----
|
||||||
|
|
||||||
1) Compile and install ifchd and ndhc.
|
1) Compile and install ifchd and ndhc.
|
||||||
|
a) gmake
|
||||||
|
b) Install the build/ifchd and build/ndhc executables in a normal place. I
|
||||||
|
would suggest /usr/sbin or /usr/local/sbin.
|
||||||
|
|
||||||
|
1alt) Compile and install ifchd and ndhc.
|
||||||
a) Create a build directory:
|
a) Create a build directory:
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
b) Create the makefiles:
|
b) Create the makefiles:
|
||||||
@ -129,8 +136,7 @@ esac
|
|||||||
directions, the script will of course require modifications.
|
directions, the script will of course require modifications.
|
||||||
|
|
||||||
4o) If you encounter problems, I suggest running both ifchd and ndhc in the
|
4o) If you encounter problems, I suggest running both ifchd and ndhc in the
|
||||||
foreground, and perhaps compiling ndhc with extra debugging output
|
foreground and examining the printed output.
|
||||||
(uncomment DEBUG=1 in the Makefile).
|
|
||||||
|
|
||||||
|
|
||||||
BEHAVIOR NOTES
|
BEHAVIOR NOTES
|
||||||
@ -144,31 +150,38 @@ ifchd can be set such that it only allows clients to configure particular
|
|||||||
network interfaces. The --interface (-i) argument does the trick, and may
|
network interfaces. The --interface (-i) argument does the trick, and may
|
||||||
be used multiple times to allow multiple interfaces.
|
be used multiple times to allow multiple interfaces.
|
||||||
|
|
||||||
GRSECURITY NOTES
|
|
||||||
----------------
|
|
||||||
|
|
||||||
Make sure that CONFIG_GRKERNSEC_CHROOT_CAPS is disabled. Otherwise, ifchd will
|
|
||||||
lose its capabilities (in particular, the ability to reconfigure interfaces)
|
|
||||||
when it chroots.
|
|
||||||
|
|
||||||
|
|
||||||
PORTING NOTES
|
PORTING NOTES
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
There are seven major functions that ifchd depends upon that are not generally
|
ndhc is rather platform-dependent, and it extensively uses Linux-specific
|
||||||
portable. First, it uses the SO_PEERCRED flag of getsockopt() to discriminate
|
features. Some of these features are also available on the BSDs.
|
||||||
authorized connections by uid, gid, and pid. Similar functionality exists in
|
|
||||||
at least the BSDs; however, it has a different API. Second, ifchd takes
|
|
||||||
advantage of Linux capabilities so that it does not need full root privileges.
|
|
||||||
Capabilities were a proposed POSIX feature that was not made part of the
|
|
||||||
official standard, so any implemention that may exist will be system-dependent.
|
|
||||||
Third and fourth, ifchd configures network interfaces and routes. Interface
|
|
||||||
and route configuration is entirely non-portable, usually requiring calls to
|
|
||||||
the catch-all ioctl(), and will almost certainly require platform-dependent
|
|
||||||
code. Fifth and sixth, both ifchd and ndhc use epoll() and signalfd(), which
|
|
||||||
are Linux-specific. Seventh, ndhc uses netlink sockets extensively for
|
|
||||||
both fetching data and hardware link state change notification events.
|
|
||||||
|
|
||||||
|
1) Both ndhc and ifchd use the SO_PEERCRED flag of getsockopt() to discriminate
|
||||||
|
authorized connections by uid, gid, and pid. Similar functionality exists in
|
||||||
|
at least the BSDs; however, it has a different API.
|
||||||
|
|
||||||
|
2) ifchd takes advantage of Linux capabilities so that it does not need full
|
||||||
|
root privileges. Capabilities were a proposed POSIX feature that was not made
|
||||||
|
part of the official standard, so any implemention that may exist will be
|
||||||
|
system-dependent.
|
||||||
|
|
||||||
|
3) ifchd configures network interfaces and routes. Interface and route
|
||||||
|
configuration is entirely non-portable, usually requiring calls to the
|
||||||
|
catch-all ioctl(), or even more unusual mechanisms like netlink sockets.
|
||||||
|
|
||||||
|
4) ndhc uses netlink sockets extensively for both fetching data and hardware
|
||||||
|
link state change notification events.
|
||||||
|
|
||||||
|
5) ndhc uses the Berkeley Packet Filter / Linux Packet Filter interfaces to
|
||||||
|
drop unwanted packets in kernelspace. This functionality is available on
|
||||||
|
most modern unix systems, but it is not standard.
|
||||||
|
|
||||||
|
6) ndhc uses epoll() and signalfd(). These are Linux-specific.
|
||||||
|
|
||||||
|
7) Numerous socket options are used, and the AF_PACKET socket family is used
|
||||||
|
for raw sockets and ARP. These are largely Linux-specific, too.
|
||||||
|
|
||||||
|
8) ndhc uses strlcpy() and strlcat(). Native versions are provided.
|
||||||
Some standard C libraries include a native implementation of strlcpy() and
|
Some standard C libraries include a native implementation of strlcpy() and
|
||||||
strlcat(). Such defines may conflict with my implementations in strl.c/strl.h.
|
strlcat(). Such defines may conflict with my implementations in strl.c/strl.h.
|
||||||
It is up to the user whether the standard C library implementations should be
|
It is up to the user whether the standard C library implementations should be
|
||||||
@ -177,3 +190,80 @@ nonstandard semantics (notably Solaris). On these systems, using the
|
|||||||
system-provided implementations may lead to security problems. Such problems
|
system-provided implementations may lead to security problems. Such problems
|
||||||
are the fault of the vendor. If you are unsure whether your system is correct
|
are the fault of the vendor. If you are unsure whether your system is correct
|
||||||
or not, I suggest using the implementation that I provide.
|
or not, I suggest using the implementation that I provide.
|
||||||
|
|
||||||
|
HISTORY
|
||||||
|
-------
|
||||||
|
|
||||||
|
I started writing ndhc back in 2004. My ISP at the time required a dhcp
|
||||||
|
client for connection authentication, and I was not comfortable with any
|
||||||
|
of the existing clients, which all ran as root and had colorful security
|
||||||
|
histories. DHCP is generally not a routed protocol, and lacks real
|
||||||
|
authentication mechanisms in real world deployments (some largely
|
||||||
|
abandoned RFCs for such behavior do exist), so no program existed to
|
||||||
|
fill the niche of a truly secure DHCP client.
|
||||||
|
|
||||||
|
My router/server at the time ran a custom Linux distro that was designed
|
||||||
|
for extreme security. A root privileged DHCP client would be nearly the
|
||||||
|
only root-owned process running on the machine, so I was highly motivated
|
||||||
|
to develop an alternative.
|
||||||
|
|
||||||
|
ifchd was first written entirely from scratch. It did not take long to write,
|
||||||
|
since it is by design rather simple, and I was already familiar with
|
||||||
|
the quirks of Linux capabilities. That left me with the choice of adapting
|
||||||
|
an existing DHCP client or writing my own from scratch.
|
||||||
|
|
||||||
|
At the time, I just wanted something that would work, so my choice was to
|
||||||
|
adapt udhcpc to work with ifchd. udhcpc was chosen since it was intended to
|
||||||
|
be used with resource-constrained or embedded systems, and was thus very
|
||||||
|
small. ISC dhclient was another alternative, but it is an extremely large
|
||||||
|
program, and it would have been very hard to audit it for correctness.
|
||||||
|
|
||||||
|
udhcpc was not all that great of a choice, since it was designed to be small at
|
||||||
|
all costs, sacrificing correctness when necessary. The code was hard to
|
||||||
|
follow, and had many quirks. Bounds-checking was rare, type aliasing common,
|
||||||
|
and state transitions were convoluted. Not all of the client was asynchronous,
|
||||||
|
and no precautions were taken against conflicting peers. ARP was not used at
|
||||||
|
all.
|
||||||
|
|
||||||
|
However, it was small. With a lot of work, I ripped out the script-calling
|
||||||
|
mechanisms and replaced them with ifchd requests. Bounds-checking was
|
||||||
|
aggressively (and somewhat hamfistedly) retrofitted into the code. It was
|
||||||
|
cleaned to a degree, and importantly it worked for connecting to my ISP.
|
||||||
|
|
||||||
|
Then I changed ISPs. My new ISP used PPPoE, not dhcp. Around the same time, I
|
||||||
|
also switched to using Gentoo rather than a hand-built distribution. I didn't
|
||||||
|
have time to maintain the old custom setup, and it was very hard keeping up
|
||||||
|
with library vulnerabilties in eg, zlib or openssl, and ensuring that all
|
||||||
|
installed binaries, dynamic and static, were updated. ndhc was abandoned for
|
||||||
|
many years. It wasn't needed on my server, and it was "too much effort" to
|
||||||
|
deviate from the stock distro dhcp clients on other machines.
|
||||||
|
|
||||||
|
Then, around 2008, I changed ISPs again. This time my new ISP used dhcp and
|
||||||
|
not PPPoE. So, after a few months, I decided to dust off the old ndhc/ifchd
|
||||||
|
project and adapt it to my modern standards and machines.
|
||||||
|
|
||||||
|
ifchd was in good shape and required little work. I ended up rewriting
|
||||||
|
ndhc. The only parts that remained from the original were the parts that
|
||||||
|
I had already rewritten before, and some of those were rewritten, too.
|
||||||
|
|
||||||
|
The end result is a modern DHCP client is largely RFC-compliant, except where
|
||||||
|
the RFCs dictate behavior that would be problematic, overly complex, useless,
|
||||||
|
or exploitable. DHCP is poorly specified, and real-world servers and clients
|
||||||
|
vary a lot from the RFCs, so these conditions are necessary for a useful
|
||||||
|
program.
|
||||||
|
|
||||||
|
Although ndhc's implementation and behavior are different, I have to credit
|
||||||
|
the idea of using netlink events to discover hardware link status transitions
|
||||||
|
to Stefan Rompf and his 'dhcpclient' program. The Linux netlink events that
|
||||||
|
are used are otherwise rather obscure and poorly documented, and I wouldn't
|
||||||
|
have known about them otherwise.
|
||||||
|
|
||||||
|
GRSECURITY NOTES
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Make sure that CONFIG_GRKERNSEC_CHROOT_CAPS is disabled. Otherwise, ifchd will
|
||||||
|
lose its capabilities (in particular, the ability to reconfigure interfaces)
|
||||||
|
when it chroots.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,7 +38,6 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
|
|
||||||
#include "ifchd-defines.h"
|
#include "ifchd-defines.h"
|
||||||
|
93
ndhc.sh
93
ndhc.sh
@ -1,93 +0,0 @@
|
|||||||
# Copyright (c) 2007-2008 Roy Marples <roy@marples.name>
|
|
||||||
# All rights reserved. Released under the 2-clause BSD license.
|
|
||||||
|
|
||||||
ndhc_depend()
|
|
||||||
{
|
|
||||||
program start /sbin/ndhc
|
|
||||||
after interface
|
|
||||||
provide dhcp
|
|
||||||
}
|
|
||||||
|
|
||||||
_config_vars="$_config_vars dhcp ndhc"
|
|
||||||
|
|
||||||
ndhc_start()
|
|
||||||
{
|
|
||||||
local args= opt= opts= pidfile="/var/run/ndhc-${IFACE}.pid"
|
|
||||||
local sendhost=true
|
|
||||||
local leasefile="/var/state/${IFACE}.lease"
|
|
||||||
|
|
||||||
eval args=\$ndhc_${IFVAR}
|
|
||||||
|
|
||||||
# Get our options
|
|
||||||
eval opts=\$dhcp_${IFVAR}
|
|
||||||
[ -z "${opts}" ] && opts=${dhcp}
|
|
||||||
|
|
||||||
# # Map some generic options to ndhc
|
|
||||||
# for opt in ${opts}; do
|
|
||||||
# case "${opt}" in
|
|
||||||
# nodns) args="${args} --env PEER_DNS=no";;
|
|
||||||
# nontp) args="${args} --env PEER_NTP=no";;
|
|
||||||
# nogateway) args="${args} --env PEER_ROUTERS=no";;
|
|
||||||
# nosendhost) sendhost=false;
|
|
||||||
# esac
|
|
||||||
# done
|
|
||||||
|
|
||||||
# [ "${metric:-0}" != "0" ] && args="${args} --env IF_METRIC=${metric}"
|
|
||||||
|
|
||||||
ebegin "Running ndhc"
|
|
||||||
|
|
||||||
case " ${args} " in
|
|
||||||
*" --quit "*|*" -q "*) x="/sbin/ndhc";;
|
|
||||||
*) x="start-stop-daemon --start --exec /sbin/ndhc \
|
|
||||||
--pidfile ${pidfile} --";;
|
|
||||||
esac
|
|
||||||
|
|
||||||
case " ${args} " in
|
|
||||||
*" --hostname="*|*" -h "*|*" -H "*);;
|
|
||||||
*)
|
|
||||||
if ${sendhost}; then
|
|
||||||
local hname="$(hostname)"
|
|
||||||
if [ "${hname}" != "(none)" ] && [ "${hname}" != "localhost" ]; then
|
|
||||||
args="${args} --hostname='${hname}'"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# delay until carrier is up
|
|
||||||
ip link set "${IFACE}" up
|
|
||||||
ip link show "${IFACE}" | grep NO-CARRIER >/dev/null 2>&1
|
|
||||||
while [ "$?" != "1" ]; do
|
|
||||||
sleep 1
|
|
||||||
ip link show "${IFACE}" | grep NO-CARRIER >/dev/null 2>&1
|
|
||||||
done
|
|
||||||
|
|
||||||
eval "${x}" "${args}" -r `cat /etc/firewall/tmp/OLDEXTIP` \
|
|
||||||
-n -i "${IFACE}" -u "ndhc" -C "/var/lib/ndhc" \
|
|
||||||
-p "${pidfile}" -l "${leasefile}" >/dev/null
|
|
||||||
eend $? || return 1
|
|
||||||
|
|
||||||
_show_address
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
ndhc_stop()
|
|
||||||
{
|
|
||||||
local pidfile="/var/lib/ndhc/var/run/ndhc-${IFACE}.pid" opts=
|
|
||||||
[ ! -f "${pidfile}" ] && return 0
|
|
||||||
|
|
||||||
# Get our options
|
|
||||||
eval opts=\$dhcp_${IFVAR}
|
|
||||||
[ -z "${opts}" ] && opts=${dhcp}
|
|
||||||
|
|
||||||
ebegin "Stopping ndhc on ${IFACE}"
|
|
||||||
case " ${opts} " in
|
|
||||||
*" release "*)
|
|
||||||
start-stop-daemon --stop --quiet --oknodo --signal USR2 \
|
|
||||||
--exec /sbin/ndhc --pidfile "${pidfile}"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
start-stop-daemon --stop --exec /sbin/ndhc --pidfile "${pidfile}"
|
|
||||||
eend $?
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user