OpenDNSSEC-signer  2.0.2
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 "status.h"
34 #include "file.h"
35 #include "log.h"
36 #include "str.h"
37 
38 #include <errno.h>
39 #include <fcntl.h> /* fcntl() */
40 #include <stdio.h> /* fprintf() */
41 #include <string.h> /* strerror(), strncmp(), strlen(), strcpy(), strncat() */
42 #include <strings.h> /* bzero() */
43 #include <sys/select.h> /* select(), FD_ZERO(), FD_SET(), FD_ISSET(), FD_CLR() */
44 #include <sys/socket.h> /* socket(), connect(), shutdown() */
45 #include <sys/un.h>
46 #include <unistd.h> /* exit(), read(), write() */
47 
48 /* According to earlier standards, we need sys/time.h, sys/types.h, unistd.h for select() */
49 #include <sys/types.h>
50 #include <sys/time.h>
51 
52 #define SE_CLI_CMDLEN 6
53 
54 static const char* cli_str = "client";
55 
60 static void
61 usage(char* argv0, FILE* out)
62 {
63  fprintf(out, "Usage: %s [<cmd>]\n", argv0);
64  fprintf(out, "Simple command line interface to control the signer "
65  "engine daemon.\nIf no cmd is given, the tool is going "
66  "into interactive mode.\n");
67  fprintf(out, "\nBSD licensed, see LICENSE in source package for "
68  "details.\n");
69  fprintf(out, "Version %s. Report bugs to <%s>.\n",
70  PACKAGE_VERSION, PACKAGE_BUGREPORT);
71 }
72 
73 
78 static int
79 max(int a, int b)
80 {
81  return a<b ? b : a;
82 }
83 
84 
89 static int
90 interface_run(FILE* fp, int sockfd, char* cmd)
91 {
92  int maxfdp1 = 0;
93  int stdineof = 0;
94  int i = 0;
95  int n = 0;
96  int ret = 0;
97  int cmd_written = 0;
98  int cmd_response = 0;
99  int written = 0;
100  fd_set rset;
101  char buf[ODS_SE_MAXLINE];
102 
103  FD_ZERO(&rset);
104  for(;;) {
105  /* prepare */
106  if (stdineof == 0) {
107  FD_SET(fileno(fp), &rset);
108  }
109  FD_SET(sockfd, &rset);
110  maxfdp1 = max(fileno(fp), sockfd) + 1;
111 
112  if (!cmd || cmd_written) {
113  /* interactive mode */
114  ret = select(maxfdp1, &rset, NULL, NULL, NULL);
115  if (ret < 0) {
116  if (errno != EINTR && errno != EWOULDBLOCK) {
117  ods_log_warning("[%s] interface select error: %s",
118  cli_str, strerror(errno));
119  }
120  continue;
121  }
122  } else if (cmd) {
123  /* passive mode */
124  ods_writen(sockfd, cmd, strlen(cmd));
125  cmd_written = 1;
126  stdineof = 1;
127  /* Clear the interactive mode / stdin fd from the set */
128  FD_CLR(fileno(fp), &rset);
129  continue;
130  }
131 
132  if (cmd && cmd_written && cmd_response) {
133  /* normal termination */
134  return 0;
135  }
136 
137  if (FD_ISSET(sockfd, &rset)) {
138  /* clear buffer */
139  for (i=0; i < ODS_SE_MAXLINE; i++) {
140  buf[i] = 0;
141  }
142  buf[ODS_SE_MAXLINE-1] = '\0';
143 
144  /* socket is readable */
145  if ((n = read(sockfd, buf, ODS_SE_MAXLINE)) <= 0) {
146  if (n < 0) {
147  /* error occurred */
148  fprintf(stderr, "error: %s\n", strerror(errno));
149  return 1;
150  } else {
151  /* n==0 */
152  if (stdineof == 1) {
153  /* normal termination */
154  return 0;
155  } else {
156  /* weird termination */
157  fprintf(stderr, "signer engine terminated "
158  "prematurely\n");
159  return 1;
160  }
161  }
162  }
163 
164  if (cmd) {
165  if (n < SE_CLI_CMDLEN) {
166  /* not enough data received */
167  fprintf(stderr, "not enough response data received "
168  "from daemon.\n");
169  return 1;
170  }
171  /* n >= SE_CLI_CMDLEN : and so it is safe to do buffer
172  manipulations below. */
173  if (strncmp(buf+n-SE_CLI_CMDLEN,"\ncmd> ",SE_CLI_CMDLEN) == 0) {
174  /* we have the full response */
175  n -= SE_CLI_CMDLEN;
176  buf[n] = '\0';
177  cmd_response = 1;
178  }
179  } else {
180  /* always null terminate string */
181  buf[n] = '\0';
182  }
183 
184  /* n > 0 : when we get to this line... */
185  for (written=0; written < n; written += ret) {
186  /* write what we got to stdout */
187  ret = (int) write(fileno(stdout), &buf[written], n-written);
188  /* error and shutdown handling */
189  if (ret == 0) {
190  fprintf(stderr, "no write\n");
191  break;
192  }
193  if (ret < 0) {
194  if (errno == EINTR || errno == EWOULDBLOCK) {
195  ret = 0;
196  continue; /* try again... */
197  }
198  fprintf(stderr, "\n\nwrite error: %s\n", strerror(errno));
199  break;
200  }
201  /* ret > 0 : when we get here... */
202  if (written+ret > n) {
203  fprintf(stderr, "\n\nwrite error: more bytes (%d) written "
204  "than required (%d)\n",
205  written+ret, n);
206  break;
207  }
208  /* written+ret < n : means partial write, requires us to loop... */
209  }
210  if (ods_strcmp(buf, ODS_SE_STOP_RESPONSE) == 0 || cmd_response) {
211  fprintf(stdout, "\n");
212  return 0;
213  }
214  }
215 
216  if (FD_ISSET(fileno(fp), &rset)) {
217  /* input is readable */
218 
219  if (cmd && cmd_written) {
220  /* passive mode */
221  stdineof = 1;
222  ret = shutdown(sockfd, SHUT_WR);
223  if (ret != 0) {
224  fprintf(stderr, "shutdown failed: %s\n",
225  strerror(errno));
226  return 1;
227  }
228  FD_CLR(fileno(fp), &rset);
229  continue;
230  }
231 
232  /* clear buffer */
233  for (i=0; i< ODS_SE_MAXLINE; i++) {
234  buf[i] = 0;
235  }
236 
237  /* interactive mode */
238  if ((n = read(fileno(fp), buf, ODS_SE_MAXLINE)) == 0) {
239  stdineof = 1;
240  ret = shutdown(sockfd, SHUT_WR);
241  if (ret != 0) {
242  fprintf(stderr, "shutdown failed: %s\n",
243  strerror(errno));
244  return 1;
245  }
246  FD_CLR(fileno(fp), &rset);
247  continue;
248  }
249 
250  buf[ODS_SE_MAXLINE-1] = '\0';
251  if (strncmp(buf, "exit", 4) == 0 ||
252  strncmp(buf, "quit", 4) == 0) {
253  return 0;
254  }
255  ods_str_trim(buf, 1);
256  n = strlen(buf);
257  ods_writen(sockfd, buf, n);
258  }
259  }
260  return 0;
261 }
262 
263 
268 static int
269 interface_start(char* cmd)
270 {
271  int sockfd, ret, flags;
272  struct sockaddr_un servaddr;
273  const char* servsock_filename = ODS_SE_SOCKFILE;
274 
275  ods_log_init("ods-signerd", 0, NULL, 0);
276 
277  /* new socket */
278  sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
279  if (sockfd < 0) {
280  fprintf(stderr, "Unable to connect to engine. "
281  "socket() failed: %s\n", strerror(errno));
282  return 1;
283  }
284 
285  /* no suprises */
286  bzero(&servaddr, sizeof(servaddr));
287  servaddr.sun_family = AF_UNIX;
288  strncpy(servaddr.sun_path, servsock_filename, sizeof(servaddr.sun_path)-1);
289 
290  /* connect */
291  ret = connect(sockfd, (const struct sockaddr*) &servaddr,
292  sizeof(servaddr));
293  if (ret != 0) {
294  if (cmd && ods_strcmp(cmd, "start\n") == 0) {
295  close(sockfd);
296  if (system(ODS_SE_ENGINE)) {
297  fprintf(stderr, "Failed to start signer engine\n");
298  return 1;
299  }
300  return 0;
301  }
302 
303  if (cmd && ods_strcmp(cmd, "running\n") == 0) {
304  fprintf(stderr, "Engine not running.\n");
305  } else {
306  fprintf(stderr, "Unable to connect to engine: "
307  "connect() failed: %s\n", strerror(errno));
308  }
309 
310  close(sockfd);
311  return 1;
312  }
313 
314  /* set socket to non-blocking */
315  flags = fcntl(sockfd, F_GETFL, 0);
316  if (flags < 0) {
317  ods_log_error("[%s] unable to start interface, fcntl(F_GETFL) "
318  "failed: %s", cli_str, strerror(errno));
319  close(sockfd);
320  return 1;
321  }
322  flags |= O_NONBLOCK;
323  if (fcntl(sockfd, F_SETFL, flags) < 0) {
324  ods_log_error("[%s] unable to start interface, fcntl(F_SETFL) "
325  "failed: %s", cli_str, strerror(errno));
326  close(sockfd);
327  return 1;
328  }
329 
330  /* some sort of interface */
331  if (!cmd) {
332  fprintf(stderr, "cmd> ");
333  }
334 
335  /* run */
336  ret = interface_run(stdin, sockfd, cmd);
337  close(sockfd);
338  return ret;
339 }
340 
341 
346 int
347 main(int argc, char* argv[])
348 {
349  int c;
350  int options_size = 0;
351  const char* options[5];
352  char* argv0;
353  char* cmd = NULL;
354  int ret = 0;
355 
356  /* Get the name of the program */
357  if((argv0 = strrchr(argv[0],'/')) == NULL)
358  argv0 = argv[0];
359  else
360  ++argv0;
361 
362  if (argc > 5) {
363  fprintf(stderr,"error, too many arguments (%d)\n", argc);
364  exit(1);
365  }
366 
367  /* command line options */
368  for (c = 0; c < argc; c++) {
369  options[c] = argv[c];
370  if (c > 0) {
371  options_size += strlen(argv[c]) + 1;
372  }
373  }
374  if (argc > 1) {
375  CHECKALLOC(cmd = (char*) malloc((options_size+2)*sizeof(char)));
376  if (!cmd) {
377  fprintf(stderr, "memory allocation failed\n");
378  exit(1);
379  }
380  (void)strncpy(cmd, "", 1);
381  for (c = 1; c < argc; c++) {
382  (void)strncat(cmd, options[c], strlen(options[c]));
383  (void)strncat(cmd, " ", 1);
384  }
385  cmd[options_size-1] = '\n';
386  }
387 
388  /* main stuff */
389  if (cmd && ods_strcmp(cmd, "-h\n") == 0) {
390  usage(argv0, stdout);
391  ret = 1;
392  } else if (cmd && ods_strcmp(cmd, "--help\n") == 0) {
393  usage(argv0, stdout);
394  ret = 1;
395  } else {
396  ret = interface_start(cmd);
397  }
398 
399  /* done */
400  free(cmd);
401  return ret;
402 }
#define SE_CLI_CMDLEN
Definition: ods-signer.c:52
int main(int argc, char *argv[])
Definition: ods-signer.c:347