OpenDNSSEC-signer  2.1.7
ods-signer.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 
61 static const char* PROMPT = "cmd> ";
62 static const char* cli_str = "client";
63 
68 static void
69 usage(char* argv0, FILE* out)
70 {
71  fprintf(out, "Usage: %s [<cmd>]\n", argv0);
72  fprintf(out, "Simple command line interface to control the signer "
73  "engine daemon.\nIf no cmd is given, the tool is going "
74  "into interactive mode.\n");
75 
76  fprintf(out, "\nSupported options:\n");
77  fprintf(out, " -h | --help Show this help and exit.\n");
78  fprintf(out, " -V | --version Show version and exit.\n");
79  fprintf(out, " -s | --socket <file> Daemon socketfile \n"
80  " | (default %s).\n", ODS_SE_SOCKFILE);
81 
82  fprintf(out, "\nBSD licensed, see LICENSE in source package for "
83  "details.\n");
84  fprintf(out, "Version %s. Report bugs to <%s>.\n",
85  PACKAGE_VERSION, PACKAGE_BUGREPORT);
86 }
87 
92 static void
93 version(FILE* out)
94 {
95  fprintf(out, "%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
96 }
97 
98 
116 /* return 0 or (1 and exit code set) or -1*/
117 static int
118 extract_msg(char* buf, int *pos, int buflen, int *exitcode, int sockfd)
119 {
120  char data[ODS_SE_MAXLINE+1], opc;
121  int datalen;
122 
123  assert(buf);
124  assert(pos);
125  assert(exitcode);
126  assert(*pos <= buflen);
127  assert(ODS_SE_MAXLINE >= buflen);
128 
129  while (1) {
130  /* Do we have a complete header? */
131  if (*pos < 3) return 0;
132  opc = buf[0];
133  datalen = (buf[1]<<8) | (buf[2]&0xFF);
134  datalen &= 0xFFFF; /* hopefully sooth tainted data checker */
135  if (datalen+3 <= *pos) {
136  /* a complete message */
137  memset(data, 0, ODS_SE_MAXLINE+1);
138  memcpy(data, buf+3, datalen);
139  *pos -= datalen+3;
140  memmove(buf, buf+datalen+3, *pos);
141 
142  if (opc == CLIENT_OPC_EXIT) {
143  fflush(stdout);
144  if (datalen != 1) return -1;
145  *exitcode = (int)buf[3];
146  return 1;
147  }
148  switch (opc) {
149  case CLIENT_OPC_STDOUT:
150  fprintf(stdout, "%s", data);
151  break;
152  case CLIENT_OPC_STDERR:
153  fprintf(stdout, "%s", data);
154  break;
155  case CLIENT_OPC_PROMPT:
156  fprintf(stdout, "%s", data);
157  fflush(stdout);
158  /* listen for input here */
159  if (!client_handleprompt(sockfd)) {
160  fprintf(stdout, "\n");
161  *exitcode = 300;
162  return 1;
163  }
164  default:
165  break;
166  }
167  continue;
168  } else if (datalen+3 > buflen) {
169  /* Message is not going to fit! Discard the data already
170  * received */
171  fprintf(stderr, "Daemon message to big, truncating.\n");
172  datalen -= *pos - 3;
173  buf[1] = datalen >> 8;
174  buf[2] = datalen & 0xFF;
175  *pos = 3;
176  return 0;
177  }
178  return 0; /* waiting for more data */
179  }
180 }
181 
190 static int
191 interface_start(const char* cmd, const char* servsock_filename)
192 {
193  struct sockaddr_un servaddr;
194  fd_set rset;
195  int sockfd, flags, exitcode = 0;
196  int ret, n, r, error = 0, inbuf_pos = 0;
197  char userbuf[ODS_SE_MAXLINE], inbuf[ODS_SE_MAXLINE];
198 
199  assert(servsock_filename);
200 
201  /* Create a socket */
202  if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
203  fprintf(stderr, "Socket creation failed: %s\n", strerror(errno));
204  return 200;
205  }
206  bzero(&servaddr, sizeof(servaddr));
207  servaddr.sun_family = AF_UNIX;
208  strncpy(servaddr.sun_path, servsock_filename, sizeof(servaddr.sun_path) - 1);
209 
210  if (connect(sockfd, (const struct sockaddr*) &servaddr, sizeof(servaddr)) == -1) {
211  if (cmd) {
212  if (strncmp(cmd, "start", 5) == 0) {
213  exitcode = system(ODS_SE_ENGINE);
214  if (exitcode == 0) {
215  close(sockfd);
216  return 0;
217  }
218  fprintf(stderr, "Failed to start signer engine\n");
219  close(sockfd);
220  return 1;
221  } else if (strcmp(cmd, "running\n") == 0) {
222  fprintf(stdout, "Engine not running.\n");
223  close(sockfd);
224  return 209;
225  }
226  }
227  fprintf(stderr,
228  "Unable to connect to engine. connect() failed: "
229  "%s (\"%s\")\n", strerror(errno), servsock_filename);
230  close(sockfd);
231  return 201;
232  }
233  /* set socket to non-blocking */
234  if ((flags = fcntl(sockfd, F_GETFL, 0)) == -1) {
235  ods_log_error("[%s] unable to start interface, fcntl(F_GETFL) "
236  "failed: %s", cli_str, strerror(errno));
237  close(sockfd);
238  return 202;
239  } else if (fcntl(sockfd, F_SETFL, flags|O_NONBLOCK) == -1) {
240  ods_log_error("[%s] unable to start interface, fcntl(F_SETFL) "
241  "failed: %s", cli_str, strerror(errno));
242  close(sockfd);
243  return 203;
244  }
245 
246  /* If we have a cmd send it to the daemon, otherwise display a
247  * prompt */
248  if (cmd) client_stdin(sockfd, cmd, strlen(cmd)+1);
249 
250  userbuf[0] = 0;
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, "Daemon 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-signerd","r");
349  fgets(line, 80, cmd2);
350  (void) pclose(cmd2);
351  pid_t pid = strtoul(line, NULL, 10);
352  fprintf(stdout, "pid %d\n", pid);
353  int time = 0;
354  error = 0;
355  while (pid > 0) {
356  if(kill(pid, 0) == 0){
357  sleep(1);
358  time += 1;
359  if (time>20) {
360  printf("signer needs more time to stop...\n");
361  time = 0;
362  }
363  }
364  else
365  break;
366  }
367  }
368 
369 #ifdef HAVE_READLINE
370  clear_history();
371  rl_free_undo_list();
372 #endif
373  return error;
374  }
375 
376 int
377 main(int argc, char* argv[])
378 {
379  char* argv0;
380  char* cmd = NULL;
381  char const *socketfile = ODS_SE_SOCKFILE;
382  int error, c, options_index = 0;
383  static struct option long_options[] = {
384  {"help", no_argument, 0, 'h'},
385  {"socket", required_argument, 0, 's'},
386  {"version", no_argument, 0, 'V'},
387  { 0, 0, 0, 0}
388  };
389 
390  ods_log_init("ods-signerd", 0, NULL, 0);
391 
392  /* Get the name of the program */
393  if((argv0 = strrchr(argv[0],'/')) == NULL)
394  argv0 = argv[0];
395  else
396  ++argv0;
397 
398  if (argc > 5) {
399  fprintf(stderr,"error, too many arguments (%d)\n", argc);
400  exit(1);
401  }
402 
403  /* parse the commandline. The + in the arg string tells getopt
404  * to stop parsing when an unknown command is found not starting
405  * with '-'. This is important for us, else switches inside commands
406  * would be consumed by getopt. */
407  while ((c=getopt_long(argc, argv, "+hVs:",
408  long_options, &options_index)) != -1) {
409  switch (c) {
410  case 'h':
411  usage(argv0, stdout);
412  exit(1);
413  case 's':
414  socketfile = optarg;
415  printf("sock set to %s\n", socketfile);
416  break;
417  case 'V':
418  version(stdout);
419  exit(0);
420  default:
421  /* unrecognized options
422  * getopt will report an error */
423  fprintf(stderr, "use --help for usage information\n");
424  exit(1);
425  }
426  }
427  argc -= optind;
428  argv += optind;
429  if (!socketfile) {
430  fprintf(stderr, "Enforcer socket file not set.\n");
431  return 101;
432  }
433  if (argc != 0)
434  cmd = ods_strcat_delim(argc, argv, ' ');
435  error = interface_start(cmd, socketfile);
436  free(cmd);
437  return error;
438 }
main
int main(int argc, char *argv[])
Definition: ods-signer.c:377