OpenDNSSEC-enforcer  2.0.2
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 <errno.h>
35 #include <fcntl.h> /* fcntl() */
36 #include <stdio.h> /* fprintf() */
37 #include <string.h> /* strerror(), strncmp(), strlen(), strcpy(), strncat() */
38 #include <strings.h> /* bzero() */
39 #include <sys/select.h> /* select(), FD_ZERO(), FD_SET(), FD_ISSET(), FD_CLR() */
40 #include <sys/socket.h> /* socket(), connect(), shutdown() */
41 #include <sys/un.h>
42 #include <unistd.h> /* exit(), read(), write() */
43 #include <getopt.h>
44 /* According to earlier standards, we need sys/time.h, sys/types.h, unistd.h for select() */
45 #include <sys/types.h>
46 #include <sys/time.h>
47 #include <stdlib.h>
48 #include <assert.h>
49 #ifdef HAVE_READLINE
50  /* cmd history */
51  #include <readline/readline.h>
52  #include <readline/history.h>
53 #endif
54 
55 #include "file.h"
56 #include "log.h"
57 #include "str.h"
58 #include "clientpipe.h"
59 
60 #define AUTHOR_NAME "Matthijs Mekking, Yuri Schaeffer, RenĂ© Post"
61 #define COPYRIGHT_STR "Copyright (C) 2010-2011 NLnet Labs OpenDNSSEC"
62 
63 static const char* PROMPT = "cmd> ";
64 static const char* cli_str = "client";
65 
70 static void
71 usage(char* argv0, FILE* out)
72 {
73  fprintf(out, "Usage: %s [OPTION]... [COMMAND]\n", argv0);
74  fprintf(out,
75 "Simple command line interface to control the enforcer engine \n"
76 "daemon. If no command is given, the tool is going to interactive \n"
77 "mode.When the daemon is running 'ods-enforcer help' gives a full \n"
78 "list of available commands.\n\n");
79 
80  fprintf(out, "Supported options:\n");
81  fprintf(out, " -h | --help Show this help and exit.\n");
82  fprintf(out, " -V | --version Show version and exit.\n");
83  fprintf(out, " -s | --socket <file> Daemon socketfile \n"
84  " | (default %s).\n", OPENDNSSEC_ENFORCER_SOCKETFILE);
85 
86  fprintf(out, "\nBSD licensed, see LICENSE in source package for "
87  "details.\n");
88  fprintf(out, "Version %s. Report bugs to <%s>.\n",
89  PACKAGE_VERSION, PACKAGE_BUGREPORT);
90 }
91 
96 static void
97 version(FILE* out)
98 {
99  fprintf(out, "%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
100  fprintf(out, "Written by %s.\n\n", AUTHOR_NAME);
101  fprintf(out, "%s. This is free software.\n", COPYRIGHT_STR);
102  fprintf(out, "See source files for more license information\n");
103  exit(0);
104 }
105 
123 /* return 0 or (1 and exit code set) or -1*/
124 static int
125 extract_msg(char* buf, int *pos, int buflen, int *exitcode, int sockfd)
126 {
127  char data[ODS_SE_MAXLINE+1], opc;
128  int datalen;
129 
130  assert(buf);
131  assert(pos);
132  assert(exitcode);
133  assert(*pos <= buflen);
134  assert(ODS_SE_MAXLINE >= buflen);
135 
136  while (1) {
137  /* Do we have a complete header? */
138  if (*pos < 3) return 0;
139  opc = buf[0];
140  datalen = (buf[1]<<8) | (buf[2]&0xFF);
141  if (datalen+3 <= *pos) {
142  /* a complete message */
143  memset(data, 0, ODS_SE_MAXLINE+1);
144  memcpy(data, buf+3, datalen);
145  *pos -= datalen+3;
146  memmove(buf, buf+datalen+3, *pos);
147 
148  if (opc == CLIENT_OPC_EXIT) {
149  fflush(stdout);
150  if (datalen != 1) return -1;
151  *exitcode = (int)buf[3];
152  return 1;
153  }
154  switch (opc) {
155  case CLIENT_OPC_STDOUT:
156  fprintf(stdout, "%s", data);
157  break;
158  case CLIENT_OPC_STDERR:
159  fprintf(stderr, "%s", data);
160  break;
161  case CLIENT_OPC_PROMPT:
162  fprintf(stdout, "%s", data);
163  fflush(stdout);
164  /* listen for input here */
165  if (!client_handleprompt(sockfd)) {
166  fprintf(stderr, "\n");
167  *exitcode = 300;
168  return 1;
169  }
170  default:
171  break;
172  }
173  continue;
174  } else if (datalen+3 > buflen) {
175  /* Message is not going to fit! Discard the data already
176  * received */
177  fprintf(stderr, "Daemon message to big, truncating.\n");
178  datalen -= *pos - 3;
179  buf[1] = datalen >> 8;
180  buf[2] = datalen & 0xFF;
181  *pos = 3;
182  return 0;
183  }
184  return 0; /* waiting for more data */
185  }
186 }
187 
196 static int
197 interface_start(const char* cmd, const char* servsock_filename)
198 {
199  struct sockaddr_un servaddr;
200  fd_set rset;
201  int sockfd, flags, exitcode = 0;
202  int ret, n, r, error = 0, inbuf_pos = 0;
203  char userbuf[ODS_SE_MAXLINE], inbuf[ODS_SE_MAXLINE];
204 
205  assert(servsock_filename);
206 
207  /* Create a socket */
208  if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
209  fprintf(stderr, "Socket creation failed: %s\n", strerror(errno));
210  return 200;
211  }
212  bzero(&servaddr, sizeof(servaddr));
213  servaddr.sun_family = AF_UNIX;
214  strncpy(servaddr.sun_path, servsock_filename, sizeof(servaddr.sun_path) - 1);
215 
216  if (connect(sockfd, (const struct sockaddr*) &servaddr, sizeof(servaddr)) == -1) {
217  if (cmd) {
218  if (strncmp(cmd, "start", 5) == 0) {
219  exitcode = system(ODS_EN_ENGINE);
220  if (exitcode == 0) {
221  close(sockfd);
222  return 0;
223  }
224  fprintf(stderr, "Error: Daemon reported a failure "
225  "starting. Please consult the logfiles.\n");
226  close(sockfd);
227  return exitcode;
228  } else if (strcmp(cmd, "running\n") == 0) {
229  fprintf(stdout, "Engine not running.\n");
230  close(sockfd);
231  return 209;
232  }
233  }
234  fprintf(stderr,
235  "Unable to connect to engine. connect() failed: "
236  "%s (\"%s\")\n", strerror(errno), servsock_filename);
237  close(sockfd);
238  return 201;
239  }
240  /* set socket to non-blocking */
241  if ((flags = fcntl(sockfd, F_GETFL, 0)) == -1) {
242  ods_log_error("[%s] unable to start interface, fcntl(F_GETFL) "
243  "failed: %s", cli_str, strerror(errno));
244  close(sockfd);
245  return 202;
246  } else if (fcntl(sockfd, F_SETFL, flags|O_NONBLOCK) == -1) {
247  ods_log_error("[%s] unable to start interface, fcntl(F_SETFL) "
248  "failed: %s", cli_str, strerror(errno));
249  close(sockfd);
250  return 203;
251  }
252 
253  /* If we have a cmd send it to the daemon, otherwise display a
254  * prompt */
255  if (cmd) client_stdin(sockfd, cmd, strlen(cmd)+1);
256  do {
257  if (!cmd) {
258 #ifdef HAVE_READLINE
259  char *icmd_ptr;
260  if ((icmd_ptr = readline(PROMPT)) == NULL) { /* eof */
261  printf("\n");
262  break;
263  }
264  if (snprintf(userbuf, ODS_SE_MAXLINE, "%s", icmd_ptr) >= ODS_SE_MAXLINE) {
265  break;
266  }
267  free(icmd_ptr);
268  ods_str_trim(userbuf,0);
269  if (strlen(userbuf) > 0) add_history(userbuf);
270 #else
271  fprintf(stdout, "%s", PROMPT);
272  fflush(stdout);
273  n = read(fileno(stdin), userbuf, ODS_SE_MAXLINE);
274  if (n == 0) { /* eof */
275  printf("\n");
276  break;
277  } else if (n == -1) {
278  error = 205;
279  break;
280  }
281  userbuf[n] = 0;
282  ods_str_trim(userbuf,0);
283 #endif
284  /* These commands don't go through the pipe */
285  if (strcmp(userbuf, "exit") == 0 || strcmp(userbuf, "quit") == 0)
286  break;
287  /* send cmd through pipe */
288  if (!client_stdin(sockfd, userbuf, strlen(userbuf))) {
289  /* only try start on fail to send */
290  if (strcmp(userbuf, "start") == 0) {
291  if (system(ODS_EN_ENGINE) != 0) {
292  fprintf(stderr, "Error: Daemon reported a failure starting. "
293  "Please consult the logfiles.\n");
294  error = 209;
295  }
296  continue;
297  }
298  }
299  }
300 
301  while (1) {
302  /* Clean the readset and add the pipe to the daemon */
303  FD_ZERO(&rset);
304  FD_SET(sockfd, &rset);
305 
306  ret = select(sockfd+1, &rset, NULL, NULL, NULL);
307  if (ret < 0) {
308  /* *SHRUG* just some interrupt*/
309  if (errno == EINTR) continue;
310  /* anything else is an actual error */
311  perror("select()");
312  error = 204;
313  break;
314  }
315  /* Handle data coming from the daemon */
316  if (FD_ISSET(sockfd, &rset)) { /*daemon pipe is readable*/
317  n = read(sockfd, inbuf+inbuf_pos, ODS_SE_MAXLINE-inbuf_pos);
318  if (n == 0) { /* daemon closed pipe */
319  fprintf(stderr, "[Remote closed connection]\n");
320  error = 206;
321  break;
322  } else if (n == -1) { /* an error */
323  if (errno == EAGAIN || errno == EWOULDBLOCK) continue;
324  perror("read()");
325  error = 207;
326  break;
327  }
328  inbuf_pos += n;
329  r = extract_msg(inbuf, &inbuf_pos, ODS_SE_MAXLINE, &exitcode, sockfd);
330  if (r == -1) {
331  fprintf(stderr, "Error handling message from daemon\n");
332  error = 208;
333  break;
334  } else if (r == 1) {
335  if (cmd)
336  error = exitcode;
337  else if (strlen(userbuf) != 0)
338  /* we are interactive so print response.
339  * But also suppress when no command is given. */
340  fprintf(stderr, "Daemon exit code: %d\n", exitcode);
341  break;
342  }
343  }
344  }
345  } while (error == 0 && !cmd);
346  close(sockfd);
347 #ifdef HAVE_READLINE
348  clear_history();
349  rl_free_undo_list();
350 #endif
351  return error;
352 }
353 
357 int
358 main(int argc, char* argv[])
359 {
360  char* argv0;
361  char* cmd = NULL;
362  char const *socketfile = OPENDNSSEC_ENFORCER_SOCKETFILE;
363  int error, c, options_index = 0;
364  static struct option long_options[] = {
365  {"help", no_argument, 0, 'h'},
366  {"socket", required_argument, 0, 's'},
367  {"version", no_argument, 0, 'V'},
368  { 0, 0, 0, 0}
369  };
370 
371  ods_log_init("", 0, NULL, 0);
372 
373  /* Get the name of the program */
374  if((argv0 = strrchr(argv[0],'/')) == NULL)
375  argv0 = argv[0];
376  else
377  ++argv0;
378  /* parse the commandline. The + in the arg string tells getopt
379  * to stop parsing when an unknown command is found not starting
380  * with '-'. This is important for us, else switches inside commands
381  * would be consumed by getopt. */
382  while ((c=getopt_long(argc, argv, "+hVs:",
383  long_options, &options_index)) != -1) {
384  switch (c) {
385  case 'h':
386  usage(argv0, stdout);
387  exit(0);
388  case 's':
389  socketfile = optarg;
390  printf("sock set to %s\n", socketfile);
391  break;
392  case 'V':
393  version(stdout);
394  exit(0);
395  default:
396  /* unrecognized options
397  * getopt will report an error */
398  exit(100);
399  }
400  }
401  argc -= optind;
402  argv += optind;
403  if (!socketfile) {
404  fprintf(stderr, "Enforcer socket file not set.\n");
405  return 101;
406  }
407  if (argc != 0)
408  cmd = ods_strcat_delim(argc, argv, ' ');
409  error = interface_start(cmd, socketfile);
410  free(cmd);
411  return error;
412 }
#define COPYRIGHT_STR
Definition: ods-enforcer.c:61
void ods_log_error(const char *format,...)
Definition: log.c:69
#define AUTHOR_NAME
Definition: ods-enforcer.c:60
int main(int argc, char *argv[])
Definition: ods-enforcer.c:358
char * argv0
Definition: ods-migrate.c:47