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