OpenDNSSEC-signer  1.4.10
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 (FD_ISSET(sockfd, &rset)) {
153  /* clear buffer */
154  for (i=0; i < ODS_SE_MAXLINE; i++) {
155  buf[i] = 0;
156  }
157  buf[ODS_SE_MAXLINE-1] = '\0';
158 
159  /* socket is readable */
160  if ((n = read(sockfd, buf, ODS_SE_MAXLINE)) <= 0) {
161  if (n < 0) {
162  /* error occurred */
163  fprintf(stderr, "error: %s\n", strerror(errno));
164  return 1;
165  } else {
166  /* n==0 */
167  if (stdineof == 1) {
168  /* normal termination */
169  return 0;
170  } else {
171  /* weird termination */
172  fprintf(stderr, "signer engine terminated "
173  "prematurely\n");
174  return 1;
175  }
176  }
177  }
178 
179  if (cmd) {
180  if (n < SE_CLI_CMDLEN) {
181  /* not enough data received */
182  fprintf(stderr, "not enough response data received "
183  "from daemon.\n");
184  return 1;
185  }
186  /* n >= SE_CLI_CMDLEN : and so it is safe to do buffer
187  manipulations below. */
188  if (strncmp(buf+n-SE_CLI_CMDLEN,"\ncmd> ",SE_CLI_CMDLEN) == 0) {
189  /* we have the full response */
190  n -= SE_CLI_CMDLEN;
191  buf[n] = '\0';
192  cmd_response = 1;
193  }
194  } else {
195  /* always null terminate string */
196  buf[n] = '\0';
197  }
198 
199  /* n > 0 : when we get to this line... */
200  for (written=0; written < n; written += ret) {
201  /* write what we got to stdout */
202  ret = (int) write(fileno(stdout), &buf[written], n-written);
203  /* error and shutdown handling */
204  if (ret == 0) {
205  fprintf(stderr, "no write\n");
206  break;
207  }
208  if (ret < 0) {
209  if (errno == EINTR || errno == EWOULDBLOCK) {
210  ret = 0;
211  continue; /* try again... */
212  }
213  fprintf(stderr, "\n\nwrite error: %s\n", strerror(errno));
214  break;
215  }
216  /* ret > 0 : when we get here... */
217  if (written+ret > n) {
218  fprintf(stderr, "\n\nwrite error: more bytes (%d) written "
219  "than required (%d)\n",
220  written+ret, n);
221  break;
222  }
223  /* written+ret < n : means partial write, requires us to loop... */
224  }
225  if (ods_strcmp(buf, ODS_SE_STOP_RESPONSE) == 0 || cmd_response) {
226  fprintf(stdout, "\n");
227  return 0;
228  }
229  }
230 
231  if (FD_ISSET(fileno(fp), &rset)) {
232  /* input is readable */
233 
234  if (cmd && cmd_written) {
235  /* passive mode */
236  stdineof = 1;
237  ret = shutdown(sockfd, SHUT_WR);
238  if (ret != 0) {
239  fprintf(stderr, "shutdown failed: %s\n",
240  strerror(errno));
241  return 1;
242  }
243  FD_CLR(fileno(fp), &rset);
244  continue;
245  }
246 
247  /* clear buffer */
248  for (i=0; i< ODS_SE_MAXLINE; i++) {
249  buf[i] = 0;
250  }
251 
252  /* interactive mode */
253  if ((n = read(fileno(fp), buf, ODS_SE_MAXLINE)) == 0) {
254  stdineof = 1;
255  ret = shutdown(sockfd, SHUT_WR);
256  if (ret != 0) {
257  fprintf(stderr, "shutdown failed: %s\n",
258  strerror(errno));
259  return 1;
260  }
261  FD_CLR(fileno(fp), &rset);
262  continue;
263  }
264 
265  buf[ODS_SE_MAXLINE-1] = '\0';
266  if (strncmp(buf, "exit", 4) == 0 ||
267  strncmp(buf, "quit", 4) == 0) {
268  return 0;
269  }
270  ods_str_trim(buf);
271  n = strlen(buf);
272  ods_writen(sockfd, buf, n);
273  }
274  }
275  return 0;
276 }
277 
278 
283 static int
284 interface_start(char* cmd, engineconfig_type* config)
285 {
286  int sockfd, ret, flags;
287  struct sockaddr_un servaddr;
288  const char* servsock_filename = config->clisock_filename;
289  char start_cmd[256];
290 
291  /* client ignores syslog facility or log filename */
292  ods_log_init(NULL, 0, config->verbosity);
293 
294  /* new socket */
295  sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
296  if (sockfd < 0) {
297  fprintf(stderr, "Unable to connect to engine. "
298  "socket() failed: %s\n", strerror(errno));
299  return 1;
300  }
301 
302  /* no suprises */
303  bzero(&servaddr, sizeof(servaddr));
304  servaddr.sun_family = AF_UNIX;
305  strncpy(servaddr.sun_path, servsock_filename,
306  sizeof(servaddr.sun_path) - 1);
307 
308  /* connect */
309  ret = connect(sockfd, (const struct sockaddr*) &servaddr,
310  sizeof(servaddr));
311  if (ret != 0) {
312  if (cmd && ods_strcmp(cmd, "start\n") == 0) {
313  size_t len = strlen(ODS_SE_ENGINE) + strlen(config->cfg_filename) + 5;
314  if (len < 256) {
315  (void) snprintf(start_cmd, len, "%s -c %s", ODS_SE_ENGINE,
316  config->cfg_filename);
317  close(sockfd);
318  return system(start_cmd);
319  } else {
320  fprintf(stderr, "Unable to start engine: cmd too long\n");
321  close(sockfd);
322  return 1;
323  }
324  }
325 
326  if (cmd && ods_strcmp(cmd, "running\n") == 0) {
327  fprintf(stderr, "Engine not running.\n");
328  } else {
329  fprintf(stderr, "Unable to connect to engine: "
330  "connect() failed: %s\n", strerror(errno));
331  }
332 
333  close(sockfd);
334  return 1;
335  }
336 
337  /* set socket to non-blocking */
338  flags = fcntl(sockfd, F_GETFL, 0);
339  if (flags < 0) {
340  ods_log_error("[%s] unable to start interface, fcntl(F_GETFL) "
341  "failed: %s", cli_str, strerror(errno));
342  close(sockfd);
343  return 1;
344  }
345  flags |= O_NONBLOCK;
346  if (fcntl(sockfd, F_SETFL, flags) < 0) {
347  ods_log_error("[%s] unable to start interface, fcntl(F_SETFL) "
348  "failed: %s", cli_str, strerror(errno));
349  close(sockfd);
350  return 1;
351  }
352 
353  /* some sort of interface */
354  if (!cmd) {
355  fprintf(stderr, "cmd> ");
356  }
357 
358  /* run */
359  ret = interface_run(stdin, sockfd, cmd);
360  close(sockfd);
361  return ret;
362 }
363 
364 
369 int
370 main(int argc, char* argv[])
371 {
372  int c;
373  int options_size = 0;
374  int options_count = 0;
375  const char* options[10];
376  const char* cfgfile = ODS_SE_CFGFILE;
377  int cfgfile_expected = 0;
378  engineconfig_type* config = NULL;
379  allocator_type* clialloc = NULL;
380  ods_status status;
381  char* cmd = NULL;
382  int ret = 0;
383 
384  /* command line options */
385  if (argc > 10) {
386  fprintf(stderr,"error, too many arguments (%d)\n", argc);
387  exit(1);
388  }
389  for (c = 1; c < argc; c++) {
390  /* leave out --options */
391  if (cfgfile_expected) {
392  cfgfile = argv[c];
393  cfgfile_expected = 0;
394  } else if (!ods_strcmp(argv[c], "-h")) {
395  usage(stdout);
396  exit(0);
397  } else if (!ods_strcmp(argv[c], "--help")) {
398  usage(stdout);
399  exit(0);
400  } else if (!ods_strcmp(argv[c], "-V")) {
401  version(stdout);
402  exit(0);
403  } else if (!ods_strcmp(argv[c], "--version")) {
404  version(stdout);
405  exit(0);
406  } else if (!ods_strcmp(argv[c], "-c")) {
407  cfgfile_expected = 1;
408  } else if (!ods_strcmp(argv[c], "--cfgfile")) {
409  cfgfile_expected = 1;
410  } else {
411  options[options_count] = argv[c];
412  options_size += strlen(argv[c]) + 1;
413  options_count++;
414  }
415  }
416  if (cfgfile_expected) {
417  fprintf(stderr,"error, missing config file\n");
418  exit(1);
419  }
420  clialloc = allocator_create(malloc, free);
421  if (!clialloc) {
422  fprintf(stderr,"error, malloc failed for client\n");
423  exit(1);
424  }
425  /* create signer command */
426  if (options_count) {
427  cmd = (char*) allocator_alloc(clialloc, (options_size+2)*sizeof(char));
428  if (!cmd) {
429  fprintf(stderr, "error, memory allocation failed\n");
430  exit(1);
431  }
432  (void)strncpy(cmd, "", 1);
433  for (c = 0; c < options_count; c++) {
434  (void)strncat(cmd, options[c], strlen(options[c]));
435  (void)strncat(cmd, " ", 1);
436  }
437  cmd[options_size-1] = '\n';
438  }
439  /* parse conf */
440  config = engine_config(clialloc, cfgfile, 0);
441  status = engine_config_check(config);
442  if (status != ODS_STATUS_OK) {
443  ods_log_error("[%s] cfgfile %s has errors", cli_str, cfgfile);
444  engine_config_cleanup(config);
445  if (cmd) allocator_deallocate(clialloc, (void*) cmd);
446  allocator_cleanup(clialloc);
447  return 1;
448  }
449  /* main stuff */
450  ret = interface_start(cmd, config);
451  /* done */
452  engine_config_cleanup(config);
453  if (cmd) allocator_deallocate(clialloc, (void*) cmd);
454  allocator_cleanup(clialloc);
455  return ret;
456 }
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:556
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:370
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