OpenDNSSEC-signer  1.4.1
ods-signer.c
Go to the documentation of this file.
1 /*
2  * $Id: ods-signer.c 7148 2013-06-11 14:11:43Z matthijs $
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  "into 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  FD_ZERO(&rset);
105  for(;;) {
106  /* prepare */
107  if (stdineof == 0) {
108  FD_SET(fileno(fp), &rset);
109  }
110  FD_SET(sockfd, &rset);
111  maxfdp1 = max(fileno(fp), sockfd) + 1;
112 
113  if (!cmd || cmd_written) {
114  /* interactive mode */
115  ret = select(maxfdp1, &rset, NULL, NULL, NULL);
116  if (ret < 0) {
117  if (errno != EINTR && errno != EWOULDBLOCK) {
118  ods_log_warning("[%s] interface select error: %s",
119  cli_str, strerror(errno));
120  }
121  continue;
122  }
123  } else if (cmd) {
124  /* passive mode */
125  ods_writen(sockfd, cmd, strlen(cmd));
126  cmd_written = 1;
127  stdineof = 1;
128  /* Clear the interactive mode / stdin fd from the set */
129  FD_CLR(fileno(fp), &rset);
130  continue;
131  }
132 
133  if (cmd && cmd_written && cmd_response) {
134  /* normal termination */
135  return 0;
136  }
137 
138  if (FD_ISSET(sockfd, &rset)) {
139  /* clear buffer */
140  for (i=0; i < ODS_SE_MAXLINE; i++) {
141  buf[i] = 0;
142  }
143  buf[ODS_SE_MAXLINE-1] = '\0';
144 
145  /* socket is readable */
146  if ((n = read(sockfd, buf, ODS_SE_MAXLINE)) <= 0) {
147  if (n < 0) {
148  /* error occurred */
149  fprintf(stderr, "error: %s\n", strerror(errno));
150  return 1;
151  } else {
152  /* n==0 */
153  if (stdineof == 1) {
154  /* normal termination */
155  return 0;
156  } else {
157  /* weird termination */
158  fprintf(stderr, "signer engine terminated "
159  "prematurely\n");
160  return 1;
161  }
162  }
163  }
164 
165  if (cmd) {
166  if (n < SE_CLI_CMDLEN) {
167  /* not enough data received */
168  fprintf(stderr, "not enough response data received "
169  "from daemon.\n");
170  return 1;
171  }
172  /* n >= SE_CLI_CMDLEN : and so it is safe to do buffer
173  manipulations below. */
174  if (strncmp(buf+n-SE_CLI_CMDLEN,"\ncmd> ",SE_CLI_CMDLEN) == 0) {
175  /* we have the full response */
176  n -= SE_CLI_CMDLEN;
177  buf[n] = '\0';
178  cmd_response = 1;
179  }
180  } else {
181  /* always null terminate string */
182  buf[n] = '\0';
183  }
184 
185  /* n > 0 : when we get to this line... */
186  for (written=0; written < n; written += ret) {
187  /* write what we got to stdout */
188  ret = (int) write(fileno(stdout), &buf[written], n-written);
189  /* error and shutdown handling */
190  if (ret == 0) {
191  fprintf(stderr, "no write\n");
192  break;
193  }
194  if (ret < 0) {
195  if (errno == EINTR || errno == EWOULDBLOCK) {
196  ret = 0;
197  continue; /* try again... */
198  }
199  fprintf(stderr, "\n\nwrite error: %s\n", strerror(errno));
200  break;
201  }
202  /* ret > 0 : when we get here... */
203  if (written+ret > n) {
204  fprintf(stderr, "\n\nwrite error: more bytes (%d) written "
205  "than required (%d)\n",
206  written+ret, n);
207  break;
208  }
209  /* written+ret < n : means partial write, requires us to loop... */
210  }
211  if (ods_strcmp(buf, ODS_SE_STOP_RESPONSE) == 0 || cmd_response) {
212  fprintf(stdout, "\n");
213  return 0;
214  }
215  }
216 
217  if (FD_ISSET(fileno(fp), &rset)) {
218  /* input is readable */
219 
220  if (cmd && cmd_written) {
221  /* passive mode */
222  stdineof = 1;
223  ret = shutdown(sockfd, SHUT_WR);
224  if (ret != 0) {
225  fprintf(stderr, "shutdown failed: %s\n",
226  strerror(errno));
227  return 1;
228  }
229  FD_CLR(fileno(fp), &rset);
230  continue;
231  }
232 
233  /* clear buffer */
234  for (i=0; i< ODS_SE_MAXLINE; i++) {
235  buf[i] = 0;
236  }
237 
238  /* interactive mode */
239  if ((n = read(fileno(fp), buf, ODS_SE_MAXLINE)) == 0) {
240  stdineof = 1;
241  ret = shutdown(sockfd, SHUT_WR);
242  if (ret != 0) {
243  fprintf(stderr, "shutdown failed: %s\n",
244  strerror(errno));
245  return 1;
246  }
247  FD_CLR(fileno(fp), &rset);
248  continue;
249  }
250 
251  buf[ODS_SE_MAXLINE-1] = '\0';
252  if (strncmp(buf, "exit", 4) == 0 ||
253  strncmp(buf, "quit", 4) == 0) {
254  return 0;
255  }
256  ods_str_trim(buf);
257  n = strlen(buf);
258  ods_writen(sockfd, buf, n);
259  }
260  }
261  return 0;
262 }
263 
264 
269 static int
270 interface_start(char* cmd)
271 {
272  int sockfd, ret, flags;
273  struct sockaddr_un servaddr;
274  const char* servsock_filename = ODS_SE_SOCKFILE;
275 
276  ods_log_init(NULL, 0, 0);
277 
278  /* new socket */
279  sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
280  if (sockfd < 0) {
281  fprintf(stderr, "Unable to connect to engine. "
282  "socket() failed: %s\n", strerror(errno));
283  return 1;
284  }
285 
286  /* no suprises */
287  bzero(&servaddr, sizeof(servaddr));
288  servaddr.sun_family = AF_UNIX;
289  strncpy(servaddr.sun_path, servsock_filename,
290  sizeof(servaddr.sun_path) - 1);
291 
292  /* connect */
293  ret = connect(sockfd, (const struct sockaddr*) &servaddr,
294  sizeof(servaddr));
295  if (ret != 0) {
296  if (cmd && ods_strcmp(cmd, "start\n") == 0) {
297  return system(ODS_SE_ENGINE);
298  }
299 
300  if (cmd && ods_strcmp(cmd, "running\n") == 0) {
301  fprintf(stderr, "Engine not running.\n");
302  } else {
303  fprintf(stderr, "Unable to connect to engine: "
304  "connect() failed: %s\n", strerror(errno));
305  }
306 
307  close(sockfd);
308  return 1;
309  }
310 
311  /* set socket to non-blocking */
312  flags = fcntl(sockfd, F_GETFL, 0);
313  if (flags < 0) {
314  ods_log_error("[%s] unable to start interface, fcntl(F_GETFL) "
315  "failed: %s", cli_str, strerror(errno));
316  close(sockfd);
317  return 1;
318  }
319  flags |= O_NONBLOCK;
320  if (fcntl(sockfd, F_SETFL, flags) < 0) {
321  ods_log_error("[%s] unable to start interface, fcntl(F_SETFL) "
322  "failed: %s", cli_str, strerror(errno));
323  close(sockfd);
324  return 1;
325  }
326 
327  /* some sort of interface */
328  if (!cmd) {
329  fprintf(stderr, "cmd> ");
330  }
331 
332  /* run */
333  ret = interface_run(stdin, sockfd, cmd);
334  close(sockfd);
335  return ret;
336 }
337 
338 
343 int
344 main(int argc, char* argv[])
345 {
346  int c;
347  int options_size = 0;
348  const char* options[4];
349  char* cmd = NULL;
350  int ret = 0;
351  allocator_type* clialloc = allocator_create(malloc, free);
352  if (!clialloc) {
353  fprintf(stderr,"error, malloc failed for client\n");
354  exit(1);
355  }
356 
357  if (argc > 5) {
358  fprintf(stderr,"error, too many arguments (%d)\n", argc);
359  exit(1);
360  }
361 
362  /* command line options */
363  for (c = 0; c < argc; c++) {
364  options[c] = argv[c];
365  if (c > 0) {
366  options_size += strlen(argv[c]) + 1;
367  }
368  }
369  if (argc > 1) {
370  cmd = (char*) allocator_alloc(clialloc, (options_size+2)*sizeof(char));
371  if (!cmd) {
372  fprintf(stderr, "memory allocation failed\n");
373  exit(1);
374  }
375  (void)strncpy(cmd, "", 1);
376  for (c = 1; c < argc; c++) {
377  (void)strncat(cmd, options[c], strlen(options[c]));
378  (void)strncat(cmd, " ", 1);
379  }
380  cmd[options_size-1] = '\n';
381  }
382 
383  /* main stuff */
384  if (cmd && ods_strcmp(cmd, "-h\n") == 0) {
385  usage(stdout);
386  ret = 1;
387  } else if (cmd && ods_strcmp(cmd, "--help\n") == 0) {
388  usage(stdout);
389  ret = 1;
390  } else {
391  ret = interface_start(cmd);
392  }
393 
394  /* done */
395  allocator_deallocate(clialloc, (void*) cmd);
396  allocator_cleanup(clialloc);
397  return ret;
398 }