OpenDNSSEC-signer 1.3.0
|
00001 /* 00002 * $Id: ods-signer.c 5320 2011-07-12 10:42:26Z jakob $ 00003 * 00004 * Copyright (c) 2009 NLNet Labs. All rights reserved. 00005 * 00006 * Redistribution and use in source and binary forms, with or without 00007 * modification, are permitted provided that the following conditions 00008 * are met: 00009 * 1. Redistributions of source code must retain the above copyright 00010 * notice, this list of conditions and the following disclaimer. 00011 * 2. Redistributions in binary form must reproduce the above copyright 00012 * notice, this list of conditions and the following disclaimer in the 00013 * documentation and/or other materials provided with the distribution. 00014 * 00015 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 00016 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 00017 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00018 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 00019 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00020 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 00021 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00022 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 00023 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 00024 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 00025 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00026 * 00027 */ 00028 00034 #include "config.h" 00035 #include "shared/allocator.h" 00036 #include "shared/file.h" 00037 #include "shared/log.h" 00038 00039 #include <errno.h> 00040 #include <fcntl.h> /* fcntl() */ 00041 #include <stdio.h> /* fprintf() */ 00042 #include <string.h> /* strerror(), strncmp(), strlen(), strcpy(), strncat() */ 00043 #include <strings.h> /* bzero() */ 00044 #include <sys/select.h> /* select(), FD_ZERO(), FD_SET(), FD_ISSET(), FD_CLR() */ 00045 #include <sys/socket.h> /* socket(), connect(), shutdown() */ 00046 #include <sys/un.h> 00047 #include <unistd.h> /* exit(), read(), write() */ 00048 00049 /* According to earlier standards, we need sys/time.h, sys/types.h, unistd.h for select() */ 00050 #include <sys/types.h> 00051 #include <sys/time.h> 00052 00053 #define SE_CLI_CMDLEN 6 00054 00055 static const char* cli_str = "client"; 00056 00061 static void 00062 usage(FILE* out) 00063 { 00064 fprintf(out, "Usage: %s [<cmd>]\n", "ods-signer"); 00065 fprintf(out, "Simple command line interface to control the signer " 00066 "engine daemon.\nIf no cmd is given, the tool is going " 00067 "to interactive mode.\n"); 00068 fprintf(out, "\nBSD licensed, see LICENSE in source package for " 00069 "details.\n"); 00070 fprintf(out, "Version %s. Report bugs to <%s>.\n", 00071 PACKAGE_VERSION, PACKAGE_BUGREPORT); 00072 } 00073 00074 00079 static int 00080 max(int a, int b) 00081 { 00082 return a<b ? b : a; 00083 } 00084 00085 00090 static void 00091 interface_run(FILE* fp, int sockfd, char* cmd) 00092 { 00093 int maxfdp1 = 0; 00094 int stdineof = 0; 00095 int i = 0; 00096 int n = 0; 00097 int ret = 0; 00098 int cmd_written = 0; 00099 int cmd_response = 0; 00100 int written = 0; 00101 fd_set rset; 00102 char buf[ODS_SE_MAXLINE]; 00103 00104 stdineof = 0; 00105 FD_ZERO(&rset); 00106 for(;;) { 00107 /* prepare */ 00108 if (stdineof == 0) { 00109 FD_SET(fileno(fp), &rset); 00110 } 00111 FD_SET(sockfd, &rset); 00112 maxfdp1 = max(fileno(fp), sockfd) + 1; 00113 00114 if (!cmd || cmd_written) { 00115 /* interactive mode */ 00116 ret = select(maxfdp1, &rset, NULL, NULL, NULL); 00117 if (ret < 0) { 00118 if (errno != EINTR && errno != EWOULDBLOCK) { 00119 ods_log_warning("[%s] interface select error: %s", 00120 cli_str, strerror(errno)); 00121 } 00122 continue; 00123 } 00124 } else if (cmd) { 00125 /* passive mode */ 00126 ods_writen(sockfd, cmd, strlen(cmd)); 00127 cmd_written = 1; 00128 stdineof = 1; 00129 continue; 00130 } 00131 00132 if (cmd && cmd_written && cmd_response) { 00133 /* normal termination */ 00134 return; 00135 } 00136 00137 if (FD_ISSET(sockfd, &rset)) { 00138 /* clear buffer */ 00139 for (i=0; i < ODS_SE_MAXLINE; i++) { 00140 buf[i] = 0; 00141 } 00142 buf[ODS_SE_MAXLINE-1] = '\0'; 00143 00144 /* socket is readable */ 00145 if ((n = read(sockfd, buf, ODS_SE_MAXLINE)) <= 0) { 00146 if (n < 0) { 00147 /* error occurred */ 00148 fprintf(stderr, "error: %s\n", strerror(errno)); 00149 exit(1); 00150 } else { 00151 /* n==0 */ 00152 if (stdineof == 1) { 00153 /* normal termination */ 00154 return; 00155 } else { 00156 /* weird termination */ 00157 fprintf(stderr, "signer engine terminated " 00158 "prematurely\n"); 00159 exit(1); 00160 } 00161 } 00162 } 00163 00164 if (cmd) { 00165 if (n < SE_CLI_CMDLEN) { 00166 /* not enough data received */ 00167 fprintf(stderr, "not enough response data received " 00168 "from daemon.\n"); 00169 exit(1); 00170 } 00171 00172 /* n >= SE_CLI_CMDLEN : and so it is safe to do buffer 00173 manipulations below. */ 00174 if (strncmp(buf+n-SE_CLI_CMDLEN,"\ncmd> ",SE_CLI_CMDLEN) == 0) { 00175 00176 /* we have the full response */ 00177 n -= SE_CLI_CMDLEN; 00178 buf[n] = '\0'; 00179 cmd_response = 1; 00180 } 00181 } 00182 00183 /* n > 0 : when we get to this line... */ 00184 for (written=0; written < n; written += ret) { 00185 /* write what we got to stdout */ 00186 ret = (int) write(fileno(stdout), &buf[written], n-written); 00187 /* error and shutdown handling */ 00188 if (ret == 0) { 00189 fprintf(stderr, "no write\n"); 00190 break; 00191 } 00192 if (ret < 0) { 00193 if (errno == EINTR || errno == EWOULDBLOCK) { 00194 ret = 0; 00195 continue; /* try again... */ 00196 } 00197 fprintf(stderr, "\n\nwrite error: %s\n", strerror(errno)); 00198 break; 00199 } 00200 /* ret > 0 : when we get here... */ 00201 if (written+ret > n) { 00202 fprintf(stderr, "\n\nwrite error: more bytes (%d) written than required (%d)\n", 00203 written+ret, n); 00204 break; 00205 } 00206 /* written+ret < n : means partial write, requires us to loop... */ 00207 } 00208 if (ods_strcmp(buf, ODS_SE_STOP_RESPONSE) == 0 || cmd_response) { 00209 fprintf(stderr, "\n"); 00210 return; 00211 } 00212 } 00213 00214 if (FD_ISSET(fileno(fp), &rset)) { 00215 /* input is readable */ 00216 00217 if (cmd && cmd_written) { 00218 /* passive mode */ 00219 stdineof = 1; 00220 ret = shutdown(sockfd, SHUT_WR); 00221 if (ret != 0) { 00222 fprintf(stderr, "shutdown failed: %s\n", 00223 strerror(errno)); 00224 exit(1); 00225 } 00226 FD_CLR(fileno(fp), &rset); 00227 continue; 00228 } 00229 00230 /* clear buffer */ 00231 for (i=0; i< ODS_SE_MAXLINE; i++) { 00232 buf[i] = 0; 00233 } 00234 00235 /* interactive mode */ 00236 if ((n = read(fileno(fp), buf, ODS_SE_MAXLINE)) == 0) { 00237 stdineof = 1; 00238 ret = shutdown(sockfd, SHUT_WR); 00239 if (ret != 0) { 00240 fprintf(stderr, "shutdown failed: %s\n", 00241 strerror(errno)); 00242 exit(1); 00243 } 00244 FD_CLR(fileno(fp), &rset); 00245 continue; 00246 } 00247 00248 buf[ODS_SE_MAXLINE-1] = '\0'; 00249 if (strncmp(buf, "exit", 4) == 0 || 00250 strncmp(buf, "quit", 4) == 0) { 00251 return; 00252 } 00253 ods_str_trim(buf); 00254 n = strlen(buf); 00255 ods_writen(sockfd, buf, n); 00256 } 00257 } 00258 } 00259 00260 00265 static void 00266 interface_start(char* cmd) 00267 { 00268 int sockfd, ret, flags; 00269 struct sockaddr_un servaddr; 00270 const char* servsock_filename = ODS_SE_SOCKFILE; 00271 00272 /* new socket */ 00273 sockfd = socket(AF_UNIX, SOCK_STREAM, 0); 00274 if (sockfd <= 0) { 00275 fprintf(stderr, "Unable to connect to engine. " 00276 "socket() failed: %s\n", strerror(errno)); 00277 exit(1); 00278 } 00279 00280 /* no suprises */ 00281 bzero(&servaddr, sizeof(servaddr)); 00282 servaddr.sun_family = AF_UNIX; 00283 strncpy(servaddr.sun_path, servsock_filename, 00284 sizeof(servaddr.sun_path) - 1); 00285 00286 /* connect */ 00287 ret = connect(sockfd, (const struct sockaddr*) &servaddr, 00288 sizeof(servaddr)); 00289 if (ret != 0) { 00290 if (cmd && ods_strcmp(cmd, "start\n") == 0) { 00291 ret = system(ODS_SE_ENGINE); 00292 return; 00293 } 00294 00295 if (cmd && ods_strcmp(cmd, "running\n") == 0) { 00296 fprintf(stderr, "Engine not running.\n"); 00297 } else { 00298 fprintf(stderr, "Unable to connect to engine: " 00299 "connect() failed: %s\n", strerror(errno)); 00300 } 00301 00302 close(sockfd); 00303 exit(1); 00304 } 00305 00306 /* set socket to non-blocking */ 00307 flags = fcntl(sockfd, F_GETFL, 0); 00308 if (flags < 0) { 00309 ods_log_error("[%s] unable to start interface, fcntl(F_GETFL) " 00310 "failed: %s", cli_str, strerror(errno)); 00311 close(sockfd); 00312 return; 00313 } 00314 flags |= O_NONBLOCK; 00315 if (fcntl(sockfd, F_SETFL, flags) < 0) { 00316 ods_log_error("[%s] unable to start interface, fcntl(F_SETFL) " 00317 "failed: %s", cli_str, strerror(errno)); 00318 close(sockfd); 00319 return; 00320 } 00321 00322 /* set stdin to non-blocking */ 00323 flags = fcntl(fileno(stdin), F_GETFL, 0); 00324 if (flags < 0) { 00325 ods_log_error("[%s] unable to start interface, fcntl(F_GETFL) " 00326 "failed: %s", cli_str, strerror(errno)); 00327 close(sockfd); 00328 return; 00329 } 00330 flags |= O_NONBLOCK; 00331 if (fcntl(fileno(stdin), F_SETFL, flags) < 0) { 00332 ods_log_error("[%s] unable to start interface, fcntl(F_SETFL) " 00333 "failed: %s", cli_str, strerror(errno)); 00334 close(sockfd); 00335 return; 00336 } 00337 00338 /* some sort of interface */ 00339 if (!cmd) { 00340 fprintf(stderr, "cmd> "); 00341 } 00342 00343 /* run */ 00344 ods_log_init(NULL, 0, 0); 00345 interface_run(stdin, sockfd, cmd); 00346 close(sockfd); 00347 return; 00348 } 00349 00350 00355 int 00356 main(int argc, char* argv[]) 00357 { 00358 int c; 00359 int options_size = 0; 00360 const char* options[4]; 00361 char* cmd = NULL; 00362 allocator_type* clialloc = allocator_create(malloc, free); 00363 if (!clialloc) { 00364 fprintf(stderr,"error, malloc failed for client\n"); 00365 exit(1); 00366 } 00367 00368 if (argc > 3) { 00369 fprintf(stderr,"error, too many arguments\n"); 00370 exit(1); 00371 } 00372 00373 /* command line options */ 00374 for (c = 0; c < argc; c++) { 00375 options[c] = argv[c]; 00376 if (c > 0) { 00377 options_size += strlen(argv[c]) + 1; 00378 } 00379 } 00380 if (argc > 1) { 00381 cmd = (char*) allocator_alloc(clialloc, (options_size+2)*sizeof(char)); 00382 if (!cmd) { 00383 fprintf(stderr, "memory allocation failed\n"); 00384 exit(1); 00385 } 00386 (void)strncpy(cmd, "", 1); 00387 for (c = 1; c < argc; c++) { 00388 (void)strncat(cmd, options[c], strlen(options[c])); 00389 (void)strncat(cmd, " ", 1); 00390 } 00391 cmd[options_size-1] = '\n'; 00392 } 00393 00394 /* main stuff */ 00395 if (cmd && ods_strcmp(cmd, "-h\n") == 0) { 00396 usage(stdout); 00397 } else if (cmd && ods_strcmp(cmd, "--help\n") == 0) { 00398 usage(stdout); 00399 } else { 00400 interface_start(cmd); 00401 } 00402 00403 /* done */ 00404 allocator_deallocate(clialloc, (void*) cmd); 00405 allocator_cleanup(clialloc); 00406 return 0; 00407 }