OpenDNSSEC-signer  1.4.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 #include "daemon/cfg.h"
34 #include "parser/confparser.h"
35 #include "shared/allocator.h"
36 #include "shared/file.h"
37 #include "shared/log.h"
38 #include "shared/status.h"
39 
40 #include <errno.h>
41 #include <getopt.h>
42 #include <fcntl.h> /* fcntl() */
43 #include <stdio.h> /* fprintf() */
44 #include <string.h> /* strerror(), strncmp(), strlen(), strcpy(), strncat() */
45 #include <strings.h> /* bzero() */
46 #include <sys/select.h> /* select(), FD_ZERO(), FD_SET(), FD_ISSET(), FD_CLR() */
47 #include <sys/socket.h> /* socket(), connect(), shutdown() */
48 #include <sys/un.h>
49 #include <unistd.h> /* exit(), read(), write() */
50 
51 /* According to earlier standards, we need sys/time.h, sys/types.h, unistd.h for select() */
52 #include <sys/types.h>
53 #include <sys/time.h>
54 
55 #define SE_CLI_CMDLEN 6
56 
57 static const char* cli_str = "client";
58 
63 static void
64 usage(FILE* out)
65 {
66  fprintf(out, "Usage: %s [<cmd>]\n", "ods-signer");
67  fprintf(out, "Simple command line interface to control the signer "
68  "engine daemon.\nIf no cmd is given, the tool is going "
69  "into interactive mode.\n\n");
70  fprintf(out, "Supported options:\n");
71  fprintf(out, " -c | --config <cfgfile> Read configuration from file.\n");
72  fprintf(out, " -h | --help Show this help and exit.\n");
73  fprintf(out, " -V | --version Show version and exit.\n");
74  fprintf(out, "\nBSD licensed, see LICENSE in source package for "
75  "details.\n");
76  fprintf(out, "Version %s. Report bugs to <%s>.\n",
77  PACKAGE_VERSION, PACKAGE_BUGREPORT);
78 }
79 
80 
85 static void
86 version(FILE* out)
87 {
88  fprintf(out, "%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
89  exit(0);
90 }
91 
92 
97 static int
98 max(int a, int b)
99 {
100  return a<b ? b : a;
101 }
102 
103 
108 static int
109 interface_run(FILE* fp, int sockfd, char* cmd)
110 {
111  int maxfdp1 = 0;
112  int stdineof = 0;
113  int i = 0;
114  int n = 0;
115  int ret = 0;
116  int cmd_written = 0;
117  int cmd_response = 0;
118  int written = 0;
119  fd_set rset;
120  char buf[ODS_SE_MAXLINE];
121 
122  stdineof = 0;
123  FD_ZERO(&rset);
124  for(;;) {
125  /* prepare */
126  if (stdineof == 0) {
127  FD_SET(fileno(fp), &rset);
128  }
129  FD_SET(sockfd, &rset);
130  maxfdp1 = max(fileno(fp), sockfd) + 1;
131 
132  if (!cmd || cmd_written) {
133  /* interactive mode */
134  ret = select(maxfdp1, &rset, NULL, NULL, NULL);
135  if (ret < 0) {
136  if (errno != EINTR && errno != EWOULDBLOCK) {
137  ods_log_warning("[%s] interface select error: %s",
138  cli_str, strerror(errno));
139  }
140  continue;
141  }
142  } else if (cmd) {
143  /* passive mode */
144  ods_writen(sockfd, cmd, strlen(cmd));
145  cmd_written = 1;
146  stdineof = 1;
147  /* Clear the interactive mode / stdin fd from the set */
148  FD_CLR(fileno(fp), &rset);
149  continue;
150  }
151 
152  if (cmd && cmd_written && cmd_response) {
153  /* normal termination */
154  return 0;
155  }
156 
157  if (FD_ISSET(sockfd, &rset)) {
158  /* clear buffer */
159  for (i=0; i < ODS_SE_MAXLINE; i++) {
160  buf[i] = 0;
161  }
162  buf[ODS_SE_MAXLINE-1] = '\0';
163 
164  /* socket is readable */
165  if ((n = read(sockfd, buf, ODS_SE_MAXLINE)) <= 0) {
166  if (n < 0) {
167  /* error occurred */
168  fprintf(stderr, "error: %s\n", strerror(errno));
169  return 1;
170  } else {
171  /* n==0 */
172  if (stdineof == 1) {
173  /* normal termination */
174  return 0;
175  } else {
176  /* weird termination */
177  fprintf(stderr, "signer engine terminated "
178  "prematurely\n");
179  return 1;
180  }
181  }
182  }
183 
184  if (cmd) {
185  if (n < SE_CLI_CMDLEN) {
186  /* not enough data received */
187  fprintf(stderr, "not enough response data received "
188  "from daemon.\n");
189  return 1;
190  }
191  /* n >= SE_CLI_CMDLEN : and so it is safe to do buffer
192  manipulations below. */
193  if (strncmp(buf+n-SE_CLI_CMDLEN,"\ncmd> ",SE_CLI_CMDLEN) == 0) {
194  /* we have the full response */
195  n -= SE_CLI_CMDLEN;
196  buf[n] = '\0';
197  cmd_response = 1;
198  }
199  } else {
200  /* always null terminate string */
201  buf[n] = '\0';
202  }
203 
204  /* n > 0 : when we get to this line... */
205  for (written=0; written < n; written += ret) {
206  /* write what we got to stdout */
207  ret = (int) write(fileno(stdout), &buf[written], n-written);
208  /* error and shutdown handling */
209  if (ret == 0) {
210  fprintf(stderr, "no write\n");
211  break;
212  }
213  if (ret < 0) {
214  if (errno == EINTR || errno == EWOULDBLOCK) {
215  ret = 0;
216  continue; /* try again... */
217  }
218  fprintf(stderr, "\n\nwrite error: %s\n", strerror(errno));
219  break;
220  }
221  /* ret > 0 : when we get here... */
222  if (written+ret > n) {
223  fprintf(stderr, "\n\nwrite error: more bytes (%d) written "
224  "than required (%d)\n",
225  written+ret, n);
226  break;
227  }
228  /* written+ret < n : means partial write, requires us to loop... */
229  }
230  if (ods_strcmp(buf, ODS_SE_STOP_RESPONSE) == 0 || cmd_response) {
231  fprintf(stdout, "\n");
232  return 0;
233  }
234  }
235 
236  if (FD_ISSET(fileno(fp), &rset)) {
237  /* input is readable */
238 
239  if (cmd && cmd_written) {
240  /* passive mode */
241  stdineof = 1;
242  ret = shutdown(sockfd, SHUT_WR);
243  if (ret != 0) {
244  fprintf(stderr, "shutdown failed: %s\n",
245  strerror(errno));
246  return 1;
247  }
248  FD_CLR(fileno(fp), &rset);
249  continue;
250  }
251 
252  /* clear buffer */
253  for (i=0; i< ODS_SE_MAXLINE; i++) {
254  buf[i] = 0;
255  }
256 
257  /* interactive mode */
258  if ((n = read(fileno(fp), buf, ODS_SE_MAXLINE)) == 0) {
259  stdineof = 1;
260  ret = shutdown(sockfd, SHUT_WR);
261  if (ret != 0) {
262  fprintf(stderr, "shutdown failed: %s\n",
263  strerror(errno));
264  return 1;
265  }
266  FD_CLR(fileno(fp), &rset);
267  continue;
268  }
269 
270  buf[ODS_SE_MAXLINE-1] = '\0';
271  if (strncmp(buf, "exit", 4) == 0 ||
272  strncmp(buf, "quit", 4) == 0) {
273  return 0;
274  }
275  ods_str_trim(buf);
276  n = strlen(buf);
277  ods_writen(sockfd, buf, n);
278  }
279  }
280  return 0;
281 }
282 
283 
288 static int
289 interface_start(char* cmd, engineconfig_type* config)
290 {
291  int sockfd, ret, flags;
292  struct sockaddr_un servaddr;
293  const char* servsock_filename = config->clisock_filename;
294  char start_cmd[256];
295 
296  /* client ignores syslog facility or log filename */
297  ods_log_init(NULL, 0, config->verbosity);
298 
299  /* new socket */
300  sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
301  if (sockfd < 0) {
302  fprintf(stderr, "Unable to connect to engine. "
303  "socket() failed: %s\n", strerror(errno));
304  return 1;
305  }
306 
307  /* no suprises */
308  bzero(&servaddr, sizeof(servaddr));
309  servaddr.sun_family = AF_UNIX;
310  strncpy(servaddr.sun_path, servsock_filename,
311  sizeof(servaddr.sun_path) - 1);
312 
313  /* connect */
314  ret = connect(sockfd, (const struct sockaddr*) &servaddr,
315  sizeof(servaddr));
316  if (ret != 0) {
317  if (cmd && ods_strcmp(cmd, "start\n") == 0) {
318  size_t len = strlen(ODS_SE_ENGINE) + strlen(config->cfg_filename) + 5;
319  if (len < 256) {
320  (void) snprintf(start_cmd, len, "%s -c %s", ODS_SE_ENGINE,
321  config->cfg_filename);
322  close(sockfd);
323  return system(start_cmd);
324  } else {
325  fprintf(stderr, "Unable to start engine: cmd too long\n");
326  close(sockfd);
327  return 1;
328  }
329  }
330 
331  if (cmd && ods_strcmp(cmd, "running\n") == 0) {
332  fprintf(stderr, "Engine not running.\n");
333  } else {
334  fprintf(stderr, "Unable to connect to engine: "
335  "connect() failed: %s\n", strerror(errno));
336  }
337 
338  close(sockfd);
339  return 1;
340  }
341 
342  /* set socket to non-blocking */
343  flags = fcntl(sockfd, F_GETFL, 0);
344  if (flags < 0) {
345  ods_log_error("[%s] unable to start interface, fcntl(F_GETFL) "
346  "failed: %s", cli_str, strerror(errno));
347  close(sockfd);
348  return 1;
349  }
350  flags |= O_NONBLOCK;
351  if (fcntl(sockfd, F_SETFL, flags) < 0) {
352  ods_log_error("[%s] unable to start interface, fcntl(F_SETFL) "
353  "failed: %s", cli_str, strerror(errno));
354  close(sockfd);
355  return 1;
356  }
357 
358  /* some sort of interface */
359  if (!cmd) {
360  fprintf(stderr, "cmd> ");
361  }
362 
363  /* run */
364  ret = interface_run(stdin, sockfd, cmd);
365  close(sockfd);
366  return ret;
367 }
368 
369 
374 int
375 main(int argc, char* argv[])
376 {
377  int c;
378  int options_size = 0;
379  int options_count = 0;
380  const char* options[10];
381  const char* cfgfile = ODS_SE_CFGFILE;
382  int cfgfile_expected = 0;
383  engineconfig_type* config = NULL;
384  allocator_type* clialloc = NULL;
385  ods_status status;
386  char* cmd = NULL;
387  int ret = 0;
388 
389  /* command line options */
390  if (argc > 10) {
391  fprintf(stderr,"error, too many arguments (%d)\n", argc);
392  exit(1);
393  }
394  for (c = 1; c < argc; c++) {
395  /* leave out --options */
396  if (cfgfile_expected) {
397  cfgfile = argv[c];
398  cfgfile_expected = 0;
399  } else if (!ods_strcmp(argv[c], "-h")) {
400  usage(stdout);
401  exit(0);
402  } else if (!ods_strcmp(argv[c], "--help")) {
403  usage(stdout);
404  exit(0);
405  } else if (!ods_strcmp(argv[c], "-V")) {
406  version(stdout);
407  exit(0);
408  } else if (!ods_strcmp(argv[c], "--version")) {
409  version(stdout);
410  exit(0);
411  } else if (!ods_strcmp(argv[c], "-c")) {
412  cfgfile_expected = 1;
413  } else if (!ods_strcmp(argv[c], "--cfgfile")) {
414  cfgfile_expected = 1;
415  } else {
416  options[options_count] = argv[c];
417  options_size += strlen(argv[c]) + 1;
418  options_count++;
419  }
420  }
421  if (cfgfile_expected) {
422  fprintf(stderr,"error, missing config file\n");
423  exit(1);
424  }
425  clialloc = allocator_create(malloc, free);
426  if (!clialloc) {
427  fprintf(stderr,"error, malloc failed for client\n");
428  exit(1);
429  }
430  /* create signer command */
431  if (options_count) {
432  cmd = (char*) allocator_alloc(clialloc, (options_size+2)*sizeof(char));
433  if (!cmd) {
434  fprintf(stderr, "error, memory allocation failed\n");
435  exit(1);
436  }
437  (void)strncpy(cmd, "", 1);
438  for (c = 0; c < options_count; c++) {
439  (void)strncat(cmd, options[c], strlen(options[c]));
440  (void)strncat(cmd, " ", 1);
441  }
442  cmd[options_size-1] = '\n';
443  }
444  /* parse conf */
445  config = engine_config(clialloc, cfgfile, 0);
446  status = engine_config_check(config);
447  if (status != ODS_STATUS_OK) {
448  ods_log_error("[%s] cfgfile %s has errors", cli_str, cfgfile);
449  engine_config_cleanup(config);
450  if (cmd) allocator_deallocate(clialloc, (void*) cmd);
451  allocator_cleanup(clialloc);
452  return 1;
453  }
454  /* main stuff */
455  ret = interface_start(cmd, config);
456  /* done */
457  engine_config_cleanup(config);
458  if (cmd) allocator_deallocate(clialloc, (void*) cmd);
459  allocator_cleanup(clialloc);
460  return ret;
461 }
void engine_config_cleanup(engineconfig_type *config)
Definition: cfg.c:251
#define SE_CLI_CMDLEN
Definition: ods-signer.c:55
const char * cfg_filename
Definition: cfg.h:51
void * allocator_alloc(allocator_type *allocator, size_t size)
Definition: allocator.c:66
enum ods_enum_status ods_status
Definition: status.h:90
void ods_log_error(const char *format,...)
Definition: log.c:334
int ods_strcmp(const char *s1, const char *s2)
Definition: file.c:320
const char * clisock_filename
Definition: cfg.h:56
engineconfig_type * engine_config(allocator_type *allocator, const char *cfgfile, int cmdline_verbosity)
Definition: cfg.c:52
void ods_str_trim(char *str)
Definition: file.c:554
allocator_type * allocator_create(void *(*allocator)(size_t size), void(*deallocator)(void *))
Definition: allocator.c:47
ssize_t ods_writen(int fd, const void *vptr, size_t n)
Definition: file.c:265
int main(int argc, char *argv[])
Definition: ods-signer.c:375
ods_status engine_config_check(engineconfig_type *config)
Definition: cfg.c:121
void allocator_cleanup(allocator_type *allocator)
Definition: allocator.c:151
void allocator_deallocate(allocator_type *allocator, void *data)
Definition: allocator.c:135
void ods_log_init(const char *filename, int use_syslog, int verbosity)
Definition: log.c:81
void ods_log_warning(const char *format,...)
Definition: log.c:318