OpenDNSSEC-enforcer 2.1.12
ods-enforcer.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2009 NLNet Labs. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
19 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
21 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
23 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *
25 */
26
32#include "config.h"
33
34#include <signal.h>
35#include <errno.h>
36#include <fcntl.h> /* fcntl() */
37#include <stdio.h> /* fprintf() */
38#include <string.h> /* strerror(), strncmp(), strlen(), strcpy(), strncat() */
39#include <strings.h> /* bzero() */
40#include <sys/select.h> /* select(), FD_ZERO(), FD_SET(), FD_ISSET(), FD_CLR() */
41#include <sys/socket.h> /* socket(), connect(), shutdown() */
42#include <sys/un.h>
43#include <unistd.h> /* exit(), read(), write() */
44#include <getopt.h>
45/* According to earlier standards, we need sys/time.h, sys/types.h, unistd.h for select() */
46#include <sys/types.h>
47#include <sys/time.h>
48#include <stdlib.h>
49#include <assert.h>
50#ifdef HAVE_READLINE
51 /* cmd history */
52 #include <readline/readline.h>
53 #include <readline/history.h>
54#endif
55
56#include "file.h"
57#include "log.h"
58#include "str.h"
59#include "clientpipe.h"
60
61static const char* PROMPT = "cmd> ";
62static const char* cli_str = "client";
63
68static void
69usage(char* argv0, FILE* out)
70{
71 fprintf(out, "Usage: %s [OPTION]... [COMMAND]\n", argv0);
72 fprintf(out,
73"Simple command line interface to control the enforcer engine \n"
74"daemon. If no command is given, the tool is going to interactive \n"
75"mode. When the daemon is running 'ods-enforcer help' gives a full \n"
76"list of available commands.\n");
77
78 fprintf(out, "\nSupported options:\n");
79 fprintf(out, " -h | --help Show this help and exit.\n");
80 fprintf(out, " -V | --version Show version and exit.\n");
81 fprintf(out, " -s | --socket <file> Daemon socketfile \n"
82 " | (default %s).\n", OPENDNSSEC_ENFORCER_SOCKETFILE);
83
84 fprintf(out, "\nBSD licensed, see LICENSE in source package for "
85 "details.\n");
86 fprintf(out, "Version %s. Report bugs to <%s>.\n",
87 PACKAGE_VERSION, PACKAGE_BUGREPORT);
88}
89
94static void
95version(FILE* out)
96{
97 fprintf(out, "%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
98}
99
117/* return 0 or (1 and exit code set) or -1*/
118static int
119extract_msg(char* buf, int *pos, int buflen, int *exitcode, int sockfd)
120{
121 char data[ODS_SE_MAXLINE+1], opc;
122 int datalen;
123
124 assert(buf);
125 assert(pos);
126 assert(exitcode);
127 assert(*pos <= buflen);
128 assert(ODS_SE_MAXLINE >= buflen);
129
130 while (1) {
131 /* Do we have a complete header? */
132 if (*pos < 3) return 0;
133 opc = buf[0];
134 datalen = (buf[1]<<8) | (buf[2]&0xFF);
135 datalen &= 0xFFFF; /* hopefully sooth tainted data checker */
136 if (datalen+3 <= *pos) {
137 /* a complete message */
138 memset(data, 0, ODS_SE_MAXLINE+1);
139 memcpy(data, buf+3, datalen);
140 *pos -= datalen+3;
141 memmove(buf, buf+datalen+3, *pos);
142
143 if (opc == CLIENT_OPC_EXIT) {
144 fflush(stdout);
145 if (datalen != 1) return -1;
146 *exitcode = (int)buf[3];
147 return 1;
148 }
149 switch (opc) {
150 case CLIENT_OPC_STDOUT:
151 fprintf(stdout, "%s", data);
152 break;
153 case CLIENT_OPC_STDERR:
154 fprintf(stderr, "%s", data);
155 break;
156 case CLIENT_OPC_PROMPT:
157 fprintf(stdout, "%s", data);
158 fflush(stdout);
159 /* listen for input here */
160 if (!client_handleprompt(sockfd)) {
161 fprintf(stderr, "\n");
162 *exitcode = 300;
163 return 1;
164 }
165 default:
166 break;
167 }
168 continue;
169 } else if (datalen+3 > buflen) {
170 /* Message is not going to fit! Discard the data already
171 * received */
172 fprintf(stderr, "Daemon message to big, truncating.\n");
173 datalen -= *pos - 3;
174 buf[1] = datalen >> 8;
175 buf[2] = datalen & 0xFF;
176 *pos = 3;
177 return 0;
178 }
179 return 0; /* waiting for more data */
180 }
181}
182
191static int
192interface_start(const char* cmd, const char* servsock_filename)
193{
194 struct sockaddr_un servaddr;
195 fd_set rset;
196 int sockfd, flags, exitcode = 0;
197 int ret, n, r, error = 0, inbuf_pos = 0;
198 char userbuf[ODS_SE_MAXLINE], inbuf[ODS_SE_MAXLINE];
199
200 assert(servsock_filename);
201
202 /* Create a socket */
203 if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
204 fprintf(stderr, "Socket creation failed: %s\n", strerror(errno));
205 return 200;
206 }
207 bzero(&servaddr, sizeof(servaddr));
208 servaddr.sun_family = AF_UNIX;
209 strncpy(servaddr.sun_path, servsock_filename, sizeof(servaddr.sun_path) - 1);
210
211 if (connect(sockfd, (const struct sockaddr*) &servaddr, sizeof(servaddr)) == -1) {
212 if (cmd) {
213 if (strncmp(cmd, "start", 5) == 0) {
214 exitcode = system(ODS_EN_ENGINE);
215 if (exitcode == 0) {
216 close(sockfd);
217 return 0;
218 }
219 fprintf(stderr, "Error: Daemon reported a failure "
220 "starting. Please consult the logfiles.\n");
221 close(sockfd);
222 return exitcode;
223 } else if (strcmp(cmd, "running\n") == 0) {
224 fprintf(stdout, "Engine not running.\n");
225 close(sockfd);
226 return 209;
227 }
228 }
229 fprintf(stderr,
230 "Unable to connect to engine. connect() failed: "
231 "%s (\"%s\")\n", strerror(errno), servsock_filename);
232 close(sockfd);
233 return 201;
234 }
235 /* set socket to non-blocking */
236 if ((flags = fcntl(sockfd, F_GETFL, 0)) == -1) {
237 ods_log_error("[%s] unable to start interface, fcntl(F_GETFL) "
238 "failed: %s", cli_str, strerror(errno));
239 close(sockfd);
240 return 202;
241 } else if (fcntl(sockfd, F_SETFL, flags|O_NONBLOCK) == -1) {
242 ods_log_error("[%s] unable to start interface, fcntl(F_SETFL) "
243 "failed: %s", cli_str, strerror(errno));
244 close(sockfd);
245 return 203;
246 }
247
248 /* If we have a cmd send it to the daemon, otherwise display a
249 * prompt */
250 if (cmd) client_stdin(sockfd, cmd, strlen(cmd)+1);
251 do {
252 if (!cmd) {
253#ifdef HAVE_READLINE
254 char *icmd_ptr;
255 if ((icmd_ptr = readline(PROMPT)) == NULL) { /* eof */
256 printf("\n");
257 break;
258 }
259 if (snprintf(userbuf, ODS_SE_MAXLINE, "%s", icmd_ptr) >= ODS_SE_MAXLINE) {
260 break;
261 }
262 free(icmd_ptr);
263 ods_str_trim(userbuf,0);
264 if (strlen(userbuf) > 0) add_history(userbuf);
265#else
266 fprintf(stdout, "%s", PROMPT);
267 fflush(stdout);
268 n = read(fileno(stdin), userbuf, ODS_SE_MAXLINE);
269 if (n == 0) { /* eof */
270 printf("\n");
271 break;
272 } else if (n == -1) {
273 error = 205;
274 break;
275 }
276 userbuf[n] = 0;
277 ods_str_trim(userbuf,0);
278#endif
279 /* These commands don't go through the pipe */
280 if (strcmp(userbuf, "exit") == 0 || strcmp(userbuf, "quit") == 0)
281 break;
282 /* send cmd through pipe */
283 if (!client_stdin(sockfd, userbuf, strlen(userbuf))) {
284 /* only try start on fail to send */
285 if (strcmp(userbuf, "start") == 0) {
286 if (system(ODS_EN_ENGINE) != 0) {
287 fprintf(stderr, "Error: Daemon reported a failure starting. "
288 "Please consult the logfiles.\n");
289 error = 209;
290 }
291 continue;
292 }
293 }
294 }
295
296 while (1) {
297 /* Clean the readset and add the pipe to the daemon */
298 FD_ZERO(&rset);
299 FD_SET(sockfd, &rset);
300
301 ret = select(sockfd+1, &rset, NULL, NULL, NULL);
302 if (ret < 0) {
303 /* *SHRUG* just some interrupt*/
304 if (errno == EINTR) continue;
305 /* anything else is an actual error */
306 perror("select()");
307 error = 204;
308 break;
309 }
310 /* Handle data coming from the daemon */
311 if (FD_ISSET(sockfd, &rset)) { /*daemon pipe is readable*/
312 n = read(sockfd, inbuf+inbuf_pos, ODS_SE_MAXLINE-inbuf_pos);
313 if (n == 0) { /* daemon closed pipe */
314 fprintf(stderr, "[Remote closed connection]\n");
315 error = 206;
316 break;
317 } else if (n == -1) { /* an error */
318 if (errno == EAGAIN || errno == EWOULDBLOCK) continue;
319 perror("read()");
320 error = 207;
321 break;
322 }
323 inbuf_pos += n;
324 r = extract_msg(inbuf, &inbuf_pos, ODS_SE_MAXLINE, &exitcode, sockfd);
325 if (r == -1) {
326 fprintf(stderr, "Error handling message from daemon\n");
327 error = 208;
328 break;
329 } else if (r == 1) {
330 if (cmd)
331 error = exitcode;
332 else if (strlen(userbuf) != 0)
333 /* we are interactive so print response.
334 * But also suppress when no command is given. */
335 fprintf(stderr, "Command exit code: %d\n", exitcode);
336 break;
337 }
338 }
339 }
340 if (strlen(userbuf) != 0 && !strncmp(userbuf, "stop", 4))
341 break;
342 } while (error == 0 && !cmd);
343 close(sockfd);
344
345 if ((cmd && !strncmp(cmd, "stop", 4)) ||
346 (strlen(userbuf) != 0 && !strncmp(userbuf, "stop", 4))) {
347 char line[80];
348 FILE *cmd2 = popen("pgrep ods-enforcerd","r");
349 error = 0;
350 if (fgets(line, 80, cmd2)) {
351 pid_t pid = strtoul(line, NULL, 10);
352 fprintf(stdout, "pid %d\n", pid);
353 int time = 0;
354 while (pid > 0) {
355 if(kill(pid, 0) != 0) break;
356 sleep(1);
357 if (++time>20) {
358 printf("enforcer needs more time to stop...\n");
359 time = 0;
360 }
361 }
362 }
363 }
364
365#ifdef HAVE_READLINE
366 clear_history();
367 rl_free_undo_list();
368#endif
369 return error;
370}
371
372int
373main(int argc, char* argv[])
374{
375 char* argv0;
376 char* cmd = NULL;
377 char const *socketfile = OPENDNSSEC_ENFORCER_SOCKETFILE;
378 int error, c, options_index = 0;
379 static struct option long_options[] = {
380 {"help", no_argument, 0, 'h'},
381 {"socket", required_argument, 0, 's'},
382 {"version", no_argument, 0, 'V'},
383 { 0, 0, 0, 0}
384 };
385
386 ods_log_init("", 0, NULL, 0);
387
388 /* Get the name of the program */
389 if((argv0 = strrchr(argv[0],'/')) == NULL)
390 argv0 = argv[0];
391 else
392 ++argv0;
393 /* parse the commandline. The + in the arg string tells getopt
394 * to stop parsing when an unknown command is found not starting
395 * with '-'. This is important for us, else switches inside commands
396 * would be consumed by getopt. */
397 while ((c=getopt_long(argc, argv, "+hVs:",
398 long_options, &options_index)) != -1) {
399 switch (c) {
400 case 'h':
401 usage(argv0, stdout);
402 exit(0);
403 case 's':
404 socketfile = optarg;
405 printf("sock set to %s\n", socketfile);
406 break;
407 case 'V':
408 version(stdout);
409 exit(0);
410 default:
411 /* unrecognized options
412 * getopt will report an error */
413 fprintf(stderr, "use --help for usage information\n");
414 exit(100);
415 }
416 }
417 argc -= optind;
418 argv += optind;
419 if (!socketfile) {
420 fprintf(stderr, "Enforcer socket file not set.\n");
421 return 101;
422 }
423 if (argc != 0)
424 cmd = ods_strcat_delim(argc, argv, ' ');
425 error = interface_start(cmd, socketfile);
426 free(cmd);
427 return error;
428}
int main(int argc, char *argv[])
Definition: ods-enforcer.c:373
char * argv0
Definition: ods-migrate.c:48