OpenDNSSEC-signer 2.1.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
34#include<signal.h>
35#include <errno.h>
36#include <fcntl.h> /* fcntl() */
37#include <stdio.h> /* fprintf() */
38#include <string.h> /* strerror(), strncmp(), strlen(), strcpy(), strncat() */
39#include <strings.h> /* bzero() */
40#include <sys/select.h> /* select(), FD_ZERO(), FD_SET(), FD_ISSET(), FD_CLR() */
41#include <sys/socket.h> /* socket(), connect(), shutdown() */
42#include <sys/un.h>
43#include <unistd.h> /* exit(), read(), write() */
44#include <getopt.h>
45/* According to earlier standards, we need sys/time.h, sys/types.h, unistd.h for select() */
46#include <sys/types.h>
47#include <sys/time.h>
48#include <stdlib.h>
49#include <assert.h>
50#ifdef HAVE_READLINE
51 /* cmd history */
52 #include <readline/readline.h>
53 #include <readline/history.h>
54#endif
55
56#include "file.h"
57#include "log.h"
58#include "str.h"
59#include "clientpipe.h"
60
61static const char* PROMPT = "cmd> ";
62static const char* cli_str = "client";
63
68static void
69usage(char* argv0, FILE* out)
70{
71 fprintf(out, "Usage: %s [<cmd>]\n", argv0);
72 fprintf(out, "Simple command line interface to control the signer "
73 "engine daemon.\nIf no cmd is given, the tool is going "
74 "into interactive mode.\n");
75
76 fprintf(out, "\nSupported options:\n");
77 fprintf(out, " -h | --help Show this help and exit.\n");
78 fprintf(out, " -V | --version Show version and exit.\n");
79 fprintf(out, " -s | --socket <file> Daemon socketfile \n"
80 " | (default %s).\n", ODS_SE_SOCKFILE);
81
82 fprintf(out, "\nBSD licensed, see LICENSE in source package for "
83 "details.\n");
84 fprintf(out, "Version %s. Report bugs to <%s>.\n",
85 PACKAGE_VERSION, PACKAGE_BUGREPORT);
86}
87
92static void
93version(FILE* out)
94{
95 fprintf(out, "%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
96}
97
98
116/* return 0 or (1 and exit code set) or -1*/
117static int
118extract_msg(char* buf, int *pos, int buflen, int *exitcode, int sockfd)
119{
120 char data[ODS_SE_MAXLINE+1], opc;
121 int datalen;
122
123 assert(buf);
124 assert(pos);
125 assert(exitcode);
126 assert(*pos <= buflen);
127 assert(ODS_SE_MAXLINE >= buflen);
128
129 while (1) {
130 /* Do we have a complete header? */
131 if (*pos < 3) return 0;
132 opc = buf[0];
133 datalen = (buf[1]<<8) | (buf[2]&0xFF);
134 datalen &= 0xFFFF; /* hopefully sooth tainted data checker */
135 if (datalen+3 <= *pos) {
136 /* a complete message */
137 memset(data, 0, ODS_SE_MAXLINE+1);
138 memcpy(data, buf+3, datalen);
139 *pos -= datalen+3;
140 memmove(buf, buf+datalen+3, *pos);
141
142 if (opc == CLIENT_OPC_EXIT) {
143 fflush(stdout);
144 if (datalen != 1) return -1;
145 *exitcode = (int)buf[3];
146 return 1;
147 }
148 switch (opc) {
149 case CLIENT_OPC_STDOUT:
150 fprintf(stdout, "%s", data);
151 break;
152 case CLIENT_OPC_STDERR:
153 fprintf(stdout, "%s", data);
154 break;
155 case CLIENT_OPC_PROMPT:
156 fprintf(stdout, "%s", data);
157 fflush(stdout);
158 /* listen for input here */
159 if (!client_handleprompt(sockfd)) {
160 fprintf(stdout, "\n");
161 *exitcode = 300;
162 return 1;
163 }
164 default:
165 break;
166 }
167 continue;
168 } else if (datalen+3 > buflen) {
169 /* Message is not going to fit! Discard the data already
170 * received */
171 fprintf(stderr, "Daemon message to big, truncating.\n");
172 datalen -= *pos - 3;
173 buf[1] = datalen >> 8;
174 buf[2] = datalen & 0xFF;
175 *pos = 3;
176 return 0;
177 }
178 return 0; /* waiting for more data */
179 }
180}
181
190static int
191interface_start(const char* cmd, const char* servsock_filename)
192{
193 struct sockaddr_un servaddr;
194 fd_set rset;
195 int sockfd, flags, exitcode = 0;
196 int ret, n, r, error = 0, inbuf_pos = 0;
197 char userbuf[ODS_SE_MAXLINE], inbuf[ODS_SE_MAXLINE];
198
199 assert(servsock_filename);
200
201 /* Create a socket */
202 if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
203 fprintf(stderr, "Socket creation failed: %s\n", strerror(errno));
204 return 200;
205 }
206 bzero(&servaddr, sizeof(servaddr));
207 servaddr.sun_family = AF_UNIX;
208 strncpy(servaddr.sun_path, servsock_filename, sizeof(servaddr.sun_path) - 1);
209
210 if (connect(sockfd, (const struct sockaddr*) &servaddr, sizeof(servaddr)) == -1) {
211 if (cmd) {
212 if (strncmp(cmd, "start", 5) == 0) {
213 exitcode = system(ODS_SE_ENGINE);
214 if (exitcode == 0) {
215 close(sockfd);
216 return 0;
217 }
218 fprintf(stderr, "Failed to start signer engine\n");
219 close(sockfd);
220 return 1;
221 } else if (strcmp(cmd, "running\n") == 0) {
222 fprintf(stdout, "Engine not running.\n");
223 close(sockfd);
224 return 209;
225 }
226 }
227 fprintf(stderr,
228 "Unable to connect to engine. connect() failed: "
229 "%s (\"%s\")\n", strerror(errno), servsock_filename);
230 close(sockfd);
231 return 201;
232 }
233 /* set socket to non-blocking */
234 if ((flags = fcntl(sockfd, F_GETFL, 0)) == -1) {
235 ods_log_error("[%s] unable to start interface, fcntl(F_GETFL) "
236 "failed: %s", cli_str, strerror(errno));
237 close(sockfd);
238 return 202;
239 } else if (fcntl(sockfd, F_SETFL, flags|O_NONBLOCK) == -1) {
240 ods_log_error("[%s] unable to start interface, fcntl(F_SETFL) "
241 "failed: %s", cli_str, strerror(errno));
242 close(sockfd);
243 return 203;
244 }
245
246 /* If we have a cmd send it to the daemon, otherwise display a
247 * prompt */
248 if (cmd) client_stdin(sockfd, cmd, strlen(cmd)+1);
249
250 userbuf[0] = 0;
251 do {
252 if (!cmd) {
253#ifdef HAVE_READLINE
254 char *icmd_ptr;
255 if ((icmd_ptr = readline(PROMPT)) == NULL) { /* eof */
256 printf("\n");
257 break;
258 }
259 if (snprintf(userbuf, ODS_SE_MAXLINE, "%s", icmd_ptr) >= ODS_SE_MAXLINE) {
260 break;
261 }
262 free(icmd_ptr);
263 ods_str_trim(userbuf,0);
264 if (strlen(userbuf) > 0) add_history(userbuf);
265#else
266 fprintf(stdout, "%s", PROMPT);
267 fflush(stdout);
268 n = read(fileno(stdin), userbuf, ODS_SE_MAXLINE);
269 if (n == 0) { /* eof */
270 printf("\n");
271 break;
272 } else if (n == -1) {
273 error = 205;
274 break;
275 }
276 userbuf[n] = 0;
277 ods_str_trim(userbuf,0);
278#endif
279 /* These commands don't go through the pipe */
280 if (strcmp(userbuf, "exit") == 0 || strcmp(userbuf, "quit") == 0)
281 break;
282 /* send cmd through pipe */
283 if (!client_stdin(sockfd, userbuf, strlen(userbuf))) {
284 /* only try start on fail to send */
285 if (strcmp(userbuf, "start") == 0) {
286 if (system(ODS_EN_ENGINE) != 0) {
287 fprintf(stderr, "Error: Daemon reported a failure starting. "
288 "Please consult the logfiles.\n");
289 error = 209;
290 }
291 continue;
292 }
293 }
294 }
295
296 while (1) {
297 /* Clean the readset and add the pipe to the daemon */
298 FD_ZERO(&rset);
299 FD_SET(sockfd, &rset);
300
301 ret = select(sockfd+1, &rset, NULL, NULL, NULL);
302 if (ret < 0) {
303 /* *SHRUG* just some interrupt*/
304 if (errno == EINTR) continue;
305 /* anything else is an actual error */
306 perror("select()");
307 error = 204;
308 break;
309 }
310 /* Handle data coming from the daemon */
311 if (FD_ISSET(sockfd, &rset)) { /*daemon pipe is readable*/
312 n = read(sockfd, inbuf+inbuf_pos, ODS_SE_MAXLINE-inbuf_pos);
313 if (n == 0) { /* daemon closed pipe */
314 fprintf(stderr, "[Remote closed connection]\n");
315 error = 206;
316 break;
317 } else if (n == -1) { /* an error */
318 if (errno == EAGAIN || errno == EWOULDBLOCK) continue;
319 perror("read()");
320 error = 207;
321 break;
322 }
323 inbuf_pos += n;
324 r = extract_msg(inbuf, &inbuf_pos, ODS_SE_MAXLINE, &exitcode, sockfd);
325 if (r == -1) {
326 fprintf(stderr, "Error handling message from daemon\n");
327 error = 208;
328 break;
329 } else if (r == 1) {
330 if (cmd)
331 error = exitcode;
332 else if (strlen(userbuf) != 0)
333 /* we are interactive so print response.
334 * But also suppress when no command is given. */
335 fprintf(stderr, "Daemon exit code: %d\n", exitcode);
336 break;
337 }
338 }
339 }
340 if (strlen(userbuf) != 0 && !strncmp(userbuf, "stop", 4))
341 break;
342 } while (error == 0 && !cmd);
343 close(sockfd);
344
345 if ((cmd && !strncmp(cmd, "stop", 4)) ||
346 (strlen(userbuf) != 0 && !strncmp(userbuf, "stop", 4))) {
347 char line[80];
348 FILE *cmd2 = popen("pgrep ods-signerd","r");
349 fgets(line, 80, cmd2);
350 (void) pclose(cmd2);
351 pid_t pid = strtoul(line, NULL, 10);
352 fprintf(stdout, "pid %d\n", pid);
353 int time = 0;
354 error = 0;
355 while (pid > 0) {
356 if(kill(pid, 0) == 0){
357 sleep(1);
358 time += 1;
359 if (time>20) {
360 printf("signer needs more time to stop...\n");
361 time = 0;
362 }
363 }
364 else
365 break;
366 }
367 }
368
369#ifdef HAVE_READLINE
370 clear_history();
371 rl_free_undo_list();
372#endif
373 return error;
374 }
375
376int
377main(int argc, char* argv[])
378{
379 char* argv0;
380 char* cmd = NULL;
381 char const *socketfile = ODS_SE_SOCKFILE;
382 int error, c, options_index = 0;
383 static struct option long_options[] = {
384 {"help", no_argument, 0, 'h'},
385 {"socket", required_argument, 0, 's'},
386 {"version", no_argument, 0, 'V'},
387 { 0, 0, 0, 0}
388 };
389
390 ods_log_init("ods-signerd", 0, NULL, 0);
391
392 /* Get the name of the program */
393 if((argv0 = strrchr(argv[0],'/')) == NULL)
394 argv0 = argv[0];
395 else
396 ++argv0;
397
398 if (argc > 5) {
399 fprintf(stderr,"error, too many arguments (%d)\n", argc);
400 exit(1);
401 }
402
403 /* parse the commandline. The + in the arg string tells getopt
404 * to stop parsing when an unknown command is found not starting
405 * with '-'. This is important for us, else switches inside commands
406 * would be consumed by getopt. */
407 while ((c=getopt_long(argc, argv, "+hVs:",
408 long_options, &options_index)) != -1) {
409 switch (c) {
410 case 'h':
411 usage(argv0, stdout);
412 exit(1);
413 case 's':
414 socketfile = optarg;
415 printf("sock set to %s\n", socketfile);
416 break;
417 case 'V':
418 version(stdout);
419 exit(0);
420 default:
421 /* unrecognized options
422 * getopt will report an error */
423 fprintf(stderr, "use --help for usage information\n");
424 exit(1);
425 }
426 }
427 argc -= optind;
428 argv += optind;
429 if (!socketfile) {
430 fprintf(stderr, "Enforcer socket file not set.\n");
431 return 101;
432 }
433 if (argc != 0)
434 cmd = ods_strcat_delim(argc, argv, ' ');
435 error = interface_start(cmd, socketfile);
436 free(cmd);
437 return error;
438}
int main(int argc, char *argv[])
Definition: ods-signer.c:377