OpenDNSSEC-signer  1.3.14
ods-signer.c
Go to the documentation of this file.
1 /*
2  * $Id: ods-signer.c 6307 2012-05-04 09:36:03Z jerry $
3  *
4  * Copyright (c) 2009 NLNet Labs. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
21  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
23  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
25  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  */
28 
34 #include "config.h"
35 #include "shared/allocator.h"
36 #include "shared/file.h"
37 #include "shared/log.h"
38 
39 #include <errno.h>
40 #include <fcntl.h> /* fcntl() */
41 #include <stdio.h> /* fprintf() */
42 #include <string.h> /* strerror(), strncmp(), strlen(), strcpy(), strncat() */
43 #include <strings.h> /* bzero() */
44 #include <sys/select.h> /* select(), FD_ZERO(), FD_SET(), FD_ISSET(), FD_CLR() */
45 #include <sys/socket.h> /* socket(), connect(), shutdown() */
46 #include <sys/un.h>
47 #include <unistd.h> /* exit(), read(), write() */
48 
49 /* According to earlier standards, we need sys/time.h, sys/types.h, unistd.h for select() */
50 #include <sys/types.h>
51 #include <sys/time.h>
52 
53 #define SE_CLI_CMDLEN 6
54 
55 static const char* cli_str = "client";
56 
61 static void
62 usage(FILE* out)
63 {
64  fprintf(out, "Usage: %s [<cmd>]\n", "ods-signer");
65  fprintf(out, "Simple command line interface to control the signer "
66  "engine daemon.\nIf no cmd is given, the tool is going "
67  "to interactive mode.\n");
68  fprintf(out, "\nBSD licensed, see LICENSE in source package for "
69  "details.\n");
70  fprintf(out, "Version %s. Report bugs to <%s>.\n",
71  PACKAGE_VERSION, PACKAGE_BUGREPORT);
72 }
73 
74 
79 static int
80 max(int a, int b)
81 {
82  return a<b ? b : a;
83 }
84 
85 
90 static int
91 interface_run(FILE* fp, int sockfd, char* cmd)
92 {
93  int maxfdp1 = 0;
94  int stdineof = 0;
95  int i = 0;
96  int n = 0;
97  int ret = 0;
98  int cmd_written = 0;
99  int cmd_response = 0;
100  int written = 0;
101  fd_set rset;
102  char buf[ODS_SE_MAXLINE];
103 
104  stdineof = 0;
105  FD_ZERO(&rset);
106  for(;;) {
107  /* prepare */
108  if (stdineof == 0) {
109  FD_SET(fileno(fp), &rset);
110  }
111  FD_SET(sockfd, &rset);
112  maxfdp1 = max(fileno(fp), sockfd) + 1;
113 
114  if (!cmd || cmd_written) {
115  /* interactive mode */
116  ret = select(maxfdp1, &rset, NULL, NULL, NULL);
117  if (ret < 0) {
118  if (errno != EINTR && errno != EWOULDBLOCK) {
119  ods_log_warning("[%s] interface select error: %s",
120  cli_str, strerror(errno));
121  }
122  continue;
123  }
124  } else if (cmd) {
125  /* passive mode */
126  ods_writen(sockfd, cmd, strlen(cmd));
127  cmd_written = 1;
128  stdineof = 1;
129  /* Clear the interactive mode / stdin fd from the set */
130  FD_CLR(fileno(fp), &rset);
131  continue;
132  }
133 
134  if (cmd && cmd_written && cmd_response) {
135  /* normal termination */
136  return 0;
137  }
138 
139  if (FD_ISSET(sockfd, &rset)) {
140  /* clear buffer */
141  for (i=0; i < ODS_SE_MAXLINE; i++) {
142  buf[i] = 0;
143  }
144  buf[ODS_SE_MAXLINE-1] = '\0';
145 
146  /* socket is readable */
147  if ((n = read(sockfd, buf, ODS_SE_MAXLINE)) <= 0) {
148  if (n < 0) {
149  /* error occurred */
150  fprintf(stderr, "error: %s\n", strerror(errno));
151  return 1;
152  } else {
153  /* n==0 */
154  if (stdineof == 1) {
155  /* normal termination */
156  return 0;
157  } else {
158  /* weird termination */
159  fprintf(stderr, "signer engine terminated "
160  "prematurely\n");
161  return 1;
162  }
163  }
164  }
165 
166  if (cmd) {
167  if (n < SE_CLI_CMDLEN) {
168  /* not enough data received */
169  fprintf(stderr, "not enough response data received "
170  "from daemon.\n");
171  return 1;
172  }
173  /* n >= SE_CLI_CMDLEN : and so it is safe to do buffer
174  manipulations below. */
175  if (strncmp(buf+n-SE_CLI_CMDLEN,"\ncmd> ",SE_CLI_CMDLEN) == 0) {
176  /* we have the full response */
177  n -= SE_CLI_CMDLEN;
178  buf[n] = '\0';
179  cmd_response = 1;
180  }
181  }
182 
183  /* n > 0 : when we get to this line... */
184  for (written=0; written < n; written += ret) {
185  /* write what we got to stdout */
186  ret = (int) write(fileno(stdout), &buf[written], n-written);
187  /* error and shutdown handling */
188  if (ret == 0) {
189  fprintf(stderr, "no write\n");
190  break;
191  }
192  if (ret < 0) {
193  if (errno == EINTR || errno == EWOULDBLOCK) {
194  ret = 0;
195  continue; /* try again... */
196  }
197  fprintf(stderr, "\n\nwrite error: %s\n", strerror(errno));
198  break;
199  }
200  /* ret > 0 : when we get here... */
201  if (written+ret > n) {
202  fprintf(stderr, "\n\nwrite error: more bytes (%d) written than required (%d)\n",
203  written+ret, n);
204  break;
205  }
206  /* written+ret < n : means partial write, requires us to loop... */
207  }
208  if (ods_strcmp(buf, ODS_SE_STOP_RESPONSE) == 0 || cmd_response) {
209  fprintf(stdout, "\n");
210  return 0;
211  }
212  }
213 
214  if (FD_ISSET(fileno(fp), &rset)) {
215  /* input is readable */
216 
217  if (cmd && cmd_written) {
218  /* passive mode */
219  stdineof = 1;
220  ret = shutdown(sockfd, SHUT_WR);
221  if (ret != 0) {
222  fprintf(stderr, "shutdown failed: %s\n",
223  strerror(errno));
224  return 1;
225  }
226  FD_CLR(fileno(fp), &rset);
227  continue;
228  }
229 
230  /* clear buffer */
231  for (i=0; i< ODS_SE_MAXLINE; i++) {
232  buf[i] = 0;
233  }
234 
235  /* interactive mode */
236  if ((n = read(fileno(fp), buf, ODS_SE_MAXLINE)) == 0) {
237  stdineof = 1;
238  ret = shutdown(sockfd, SHUT_WR);
239  if (ret != 0) {
240  fprintf(stderr, "shutdown failed: %s\n",
241  strerror(errno));
242  return 1;
243  }
244  FD_CLR(fileno(fp), &rset);
245  continue;
246  }
247 
248  buf[ODS_SE_MAXLINE-1] = '\0';
249  if (strncmp(buf, "exit", 4) == 0 ||
250  strncmp(buf, "quit", 4) == 0) {
251  return 0;
252  }
253  ods_str_trim(buf);
254  n = strlen(buf);
255  ods_writen(sockfd, buf, n);
256  }
257  }
258 }
259 
260 
265 static int
266 interface_start(char* cmd)
267 {
268  int sockfd, ret, flags;
269  struct sockaddr_un servaddr;
270  const char* servsock_filename = ODS_SE_SOCKFILE;
271 
272  ods_log_init(NULL, 0, 0);
273 
274  /* new socket */
275  sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
276  if (sockfd < 0) {
277  fprintf(stderr, "Unable to connect to engine. "
278  "socket() failed: %s\n", strerror(errno));
279  return 1;
280  }
281 
282  /* no suprises */
283  bzero(&servaddr, sizeof(servaddr));
284  servaddr.sun_family = AF_UNIX;
285  strncpy(servaddr.sun_path, servsock_filename,
286  sizeof(servaddr.sun_path) - 1);
287 
288  /* connect */
289  ret = connect(sockfd, (const struct sockaddr*) &servaddr,
290  sizeof(servaddr));
291  if (ret != 0) {
292  if (cmd && ods_strcmp(cmd, "start\n") == 0) {
293  return system(ODS_SE_ENGINE);
294  }
295 
296  if (cmd && ods_strcmp(cmd, "running\n") == 0) {
297  fprintf(stderr, "Engine not running.\n");
298  } else {
299  fprintf(stderr, "Unable to connect to engine: "
300  "connect() failed: %s\n", strerror(errno));
301  }
302 
303  close(sockfd);
304  return 1;
305  }
306 
307  /* set socket to non-blocking */
308  flags = fcntl(sockfd, F_GETFL, 0);
309  if (flags < 0) {
310  ods_log_error("[%s] unable to start interface, fcntl(F_GETFL) "
311  "failed: %s", cli_str, strerror(errno));
312  close(sockfd);
313  return 1;
314  }
315  flags |= O_NONBLOCK;
316  if (fcntl(sockfd, F_SETFL, flags) < 0) {
317  ods_log_error("[%s] unable to start interface, fcntl(F_SETFL) "
318  "failed: %s", cli_str, strerror(errno));
319  close(sockfd);
320  return 1;
321  }
322 
323  /* some sort of interface */
324  if (!cmd) {
325  fprintf(stderr, "cmd> ");
326  }
327 
328  /* run */
329  ret = interface_run(stdin, sockfd, cmd);
330  close(sockfd);
331  return ret;
332 }
333 
334 
339 int
340 main(int argc, char* argv[])
341 {
342  int c;
343  int options_size = 0;
344  const char* options[4];
345  char* cmd = NULL;
346  int ret = 0;
347  allocator_type* clialloc = allocator_create(malloc, free);
348  if (!clialloc) {
349  fprintf(stderr,"error, malloc failed for client\n");
350  exit(1);
351  }
352 
353  if (argc > 3) {
354  fprintf(stderr,"error, too many arguments\n");
355  exit(1);
356  }
357 
358  /* command line options */
359  for (c = 0; c < argc; c++) {
360  options[c] = argv[c];
361  if (c > 0) {
362  options_size += strlen(argv[c]) + 1;
363  }
364  }
365  if (argc > 1) {
366  cmd = (char*) allocator_alloc(clialloc, (options_size+2)*sizeof(char));
367  if (!cmd) {
368  fprintf(stderr, "memory allocation failed\n");
369  exit(1);
370  }
371  (void)strncpy(cmd, "", 1);
372  for (c = 1; c < argc; c++) {
373  (void)strncat(cmd, options[c], strlen(options[c]));
374  (void)strncat(cmd, " ", 1);
375  }
376  cmd[options_size-1] = '\n';
377  }
378 
379  /* main stuff */
380  if (cmd && ods_strcmp(cmd, "-h\n") == 0) {
381  usage(stdout);
382  ret = 1;
383  } else if (cmd && ods_strcmp(cmd, "--help\n") == 0) {
384  usage(stdout);
385  ret = 1;
386  } else {
387  ret = interface_start(cmd);
388  }
389 
390  /* done */
391  allocator_deallocate(clialloc, (void*) cmd);
392  allocator_cleanup(clialloc);
393  return ret;
394 }