1 /* **************************************************************
2 Copyright (C) 2010, 2011, 2012 Hewlett-Packard Development Company, L.P.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 version 2 as published by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program; if not, write to the Free Software Foundation, Inc.,
15 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 ************************************************************** */
17 /**
18 * \file
19 * \brief CLI interface for scheduler
20 */
21
22 /* std library includes */
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 /* unix includes */
28 #include <ctype.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <pwd.h>
32 #include <sys/socket.h>
33 #include <sys/types.h>
34 #include <netinet/in.h>
35 #include <netdb.h>
36 #include <unistd.h>
37
38 /* other library includes */
39 #include <libfossology.h>
40 #include <glib.h>
41
42 #define P_WIDTH 27
43 #define F_WIDTH 10
44
45 #define vprintf(...) if(verbose) printf(__VA_ARGS__);
46
47 int response = 1; ///< Is a response expected from the scheduler
48 int s; ///< The socket that the CLI will use to communicate
49 int verbose; ///< The verbose flag for the cli
50 fo_conf* conf; ///< The loaded configuration data
51
52 /* ************************************************************************** */
53 /* **** utility functions *************************************************** */
54 /* ************************************************************************** */
55
56 /**
57 * @brief Create a socket connection
58 *
59 * Creates a new socket that connects to the given host and port.
60 *
61 * @param host Cstring name of the host to connect to
62 * @param port Cstring representation of the port to connect to
63 * @return The file descriptor of the new socket
64 */
socket_connect(char * host,char * port)65 int socket_connect(char* host, char* port)
66 {
67 int fd;
68 struct addrinfo hints;
69 struct addrinfo* servs, * curr = NULL;
70
71 memset(&hints, 0, sizeof(hints));
72 hints.ai_family = AF_UNSPEC;
73 hints.ai_socktype = SOCK_STREAM;
74 if(getaddrinfo(host, port, &hints, &servs) == -1)
75 {
76 fprintf(stderr, "ERROR: %s.%d: unable to connect to %s port: %s\n",
77 __FILE__, __LINE__, host, port);
78 fprintf(stderr, "ERROR: errno: %s\n", strerror(errno));
79 return -1;
80 }
81
82 for(curr = servs; curr != NULL; curr = curr->ai_next)
83 {
84 if((fd = socket(curr->ai_family, hints.ai_socktype, curr->ai_protocol)) < 0)
85 continue;
86
87 if(connect(fd, curr->ai_addr, curr->ai_addrlen) == -1)
88 continue;
89
90 break;
91 }
92
93 if(curr == NULL)
94 {
95 fprintf(stderr, "ERROR: %s.%d: unable to connect to %s port: %s\n",
96 __FILE__, __LINE__, host, port);
97 return -1;
98 }
99
100 freeaddrinfo(servs);
101 return fd;
102 }
103
104 /**
105 * @brief Performs the actions necessary to receive from the scheduler.
106 *
107 * @param s the socket that is connected to the scheduler
108 * @param buffer buffer that is used to store messages from the scheduler
109 * @param max the capacity of the buffer
110 * @param end used to determine when this function should return
111 * @return
112 */
receive(int s,char * buffer,size_t max,uint8_t end)113 uint8_t receive(int s, char* buffer, size_t max, uint8_t end)
114 {
115 size_t bytes = 0;
116 uint8_t closing = 0;
117 char* poss;
118
119 do
120 {
121 /* start by clearing the buffer */
122 memset(buffer, '\0', max);
123 bytes = 0;
124
125 /* read from the socket */
126 do
127 {
128 bytes = read(s, buffer + bytes, max - bytes);
129
130 if(bytes == 0)
131 {
132 printf("ERROR: connection to scheduler closed\nERROR: closing cli\n");
133 closing = 1;
134 }
135
136 bytes = strlen(buffer);
137 } while(!closing && buffer[bytes - 1] != '\n');
138
139 /* interpret the results */
140 for(poss = strtok(buffer, "\n"); !closing && poss != NULL;
141 poss = strtok(NULL, "\n"))
142 {
143 if(strncmp(poss, "received", 8) == 0)
144 {
145 if(response)
146 printf("Command received\n");
147 }
148 else if(strncmp(poss, "CLOSE", 5) == 0)
149 {
150 closing = 1;
151 }
152 else if(strcmp(poss, "end") != 0)
153 {
154 printf("%s\n", poss);
155 }
156 else
157 {
158 end = 0;
159 }
160 }
161
162 fflush(stdout);
163 } while(end);
164
165 return closing;
166 }
167
168 /**
169 * @brief Interface usage print
170 *
171 * Prints the usage message for the interface on stdout.
172 */
interface_usage()173 void interface_usage()
174 {
175 printf("FOSSology scheduler command line interface\n");
176 printf("+-----------------------------------------------------------------------------+\n");
177 printf("|%*s: EFFECT |\n", P_WIDTH, "CMD [optional] <required>");
178 printf("+-----------------------------------------------------------------------------+\n");
179 printf("|%*s: prints this usage statement |\n", P_WIDTH, "help");
180 printf("|%*s: close the connection and exit cli |\n", P_WIDTH, "close");
181 printf("|%*s: shutdown will wait for agents be stopping |\n", P_WIDTH, "stop");
182 printf("|%*s: shutdown will shutdown immediately |\n", P_WIDTH, "die");
183 printf("|%*s: get load information for host machines |\n", P_WIDTH, "load");
184 printf("|%*s: kills a currently running job (ungraceful) |\n", P_WIDTH, "kill <jq_pk> <\"message\">");
185 printf("|%*s: pauses a job indefinitely |\n", P_WIDTH, "pause <jq_pk>");
186 printf("|%*s: reload the configuration information |\n", P_WIDTH, "reload");
187 printf("|%*s: prints a list of valid agents |\n", P_WIDTH, "agents");
188 printf("|%*s: scheduler responds with status information |\n", P_WIDTH, "status [jq_pk]");
189 printf("|%*s: restart a paused job |\n", P_WIDTH, "restart <jq_pk>");
190 printf("|%*s: query/change the scheduler/job verbosity |\n", P_WIDTH, "verbose [jq_pk] [level]");
191 printf("|%*s: change priority for job that this jq_pk is in |\n", P_WIDTH, "priority <jq_pk> <level>");
192 printf("|%*s: causes the scheduler to check the job queue |\n", P_WIDTH, "database");
193 printf("+-----------------------------------------------------------------------------+\n");
194 fflush(stdout);
195 }
196
197 /* ************************************************************************** */
198 /* **** main **************************************************************** */
199 /* ************************************************************************** */
200
main(int argc,char ** argv)201 int main(int argc, char** argv)
202 {
203 /* local variables */
204 fd_set fds; // file descriptor set used in select statement
205 int closing; // flags and loop variables
206 size_t bytes; // variable to capture return of read
207 char* host; // string to hold the name of the host
208 char* port; // string port number to connect to
209 char buffer[1024]; // string buffer used to read
210 char* config; // FOSSology configuration directory
211 GOptionContext* options; // the command line options parser
212 GError* error = NULL;
213
214 /* command bool and info */
215 uint8_t c_die = 0;
216 uint8_t c_stop = 0;
217 uint8_t c_load = 0;
218 uint8_t c_pause = 0;
219 uint8_t c_reload = 0;
220 uint8_t c_status = 0;
221 uint8_t c_agents = 0;
222 uint8_t c_restart = 0;
223 uint8_t c_verbose = 0;
224 uint8_t c_database = 0;
225
226 /* initialize memory */
227 host = NULL;
228 port = NULL;
229 config = DEFAULT_SETUP;
230 memset(buffer, '\0', sizeof(buffer));
231 closing = 0;
232 verbose = 0;
233
234 GOptionEntry entries[] =
235 {
236 {"config", 'c', 0, G_OPTION_ARG_STRING, &config,
237 "Set the directory for the system configuration", "string"},
238 {"host", 'H', 0, G_OPTION_ARG_STRING, &host,
239 "Set the host that the scheduler is on", "string"},
240 {"port", 'p', 0, G_OPTION_ARG_STRING, &port,
241 "Set the port that the scheduler is listening on", "integer"},
242 {"quiet", 'q', 0, G_OPTION_ARG_NONE, &verbose,
243 "Cause the CLI to not print usage hints", NULL},
244 {"load", 'l', 0, G_OPTION_ARG_NONE, &c_load,
245 "CLI will send a load command and close"},
246 {"agents", 'a', 0, G_OPTION_ARG_NONE, &c_agents,
247 "CLI will send an agents command and close"},
248 {"status", 'S', 0, G_OPTION_ARG_NONE, &c_status,
249 "CLI will send a status command and close"},
250 {"stop", 's', 0, G_OPTION_ARG_NONE, &c_stop,
251 "CLI will send stop command and close", NULL},
252 {"die", 'D', 0, G_OPTION_ARG_NONE, &c_die,
253 "CLI will send a die command and close"},
254 {"pause", 'P', 0, G_OPTION_ARG_INT, &c_pause,
255 "CLI will send a pause command and close", "integer"},
256 {"reload", 'r', 0, G_OPTION_ARG_NONE, &c_reload,
257 "CLI will send a reload command and close", NULL},
258 {"restart", 'R', 0, G_OPTION_ARG_INT, &c_restart,
259 "CLI will send a restart command and close", "integer"},
260 {"verbose", 'v', 0, G_OPTION_ARG_INT, &c_verbose,
261 "CLI will change the scheduler's verbose level", "integer"},
262 {"database", 'd', 0, G_OPTION_ARG_NONE, &c_database,
263 "CLI will send a database command to scheduler", NULL},
264 {NULL}
265 };
266
267 options = g_option_context_new("- command line tool for FOSSology scheduler");
268 g_option_context_add_main_entries(options, entries, NULL);
269 g_option_context_set_ignore_unknown_options(options, FALSE);
270 g_option_context_parse(options, &argc, &argv, &error);
271
272 if(error)
273 {
274 config = g_option_context_get_help(options, FALSE, NULL);
275 fprintf(stderr, "ERROR: %s\n%s", error->message, config);
276 g_free(config);
277 return -1;
278 }
279
280 g_option_context_free(options);
281
282 /* set the basic configuration */
283 /* change the verbose to conform to quite option */
284 verbose = !verbose;
285
286 snprintf(buffer, sizeof(buffer), "%s/fossology.conf", config);
287 conf = fo_config_load(buffer, &error);
288 if(error)
289 {
290 fprintf(stderr, "ERROR: %s.%d: error loading config: %s\n",
291 __FILE__, __LINE__, error->message);
292 return -1;
293 }
294
295 /* check the scheduler config for port number */
296 if(port == NULL)
297 port = fo_config_get(conf, "FOSSOLOGY", "port", &error);
298 if(!error && host == NULL)
299 host = fo_config_get(conf, "FOSSOLOGY", "address", &error);
300
301 /* open the connection to the scheduler */
302 if((s = socket_connect(host, port)) < 0)
303 return -1;
304
305 /* check specific command instructions */
306 if(c_die || c_stop || c_load || c_pause || c_reload || c_status || c_agents
307 || c_restart || c_verbose || c_database)
308 {
309 response = 0;
310
311 /* simple no parameter commands */
312 if(c_reload)
313 bytes = write(s, "reload", 6);
314 if(c_database)
315 bytes = write(s, "database", 8);
316 if(c_stop)
317 bytes = write(s, "stop", 4);
318 if(c_die)
319 bytes = write(s, "die", 3);
320
321 /* simple commands that require a parameter */
322 if(c_verbose)
323 {
324 snprintf(buffer, sizeof(buffer) - 1, "verbose %d", c_verbose);
325 bytes = write(s, buffer, strlen(buffer));
326 }
327
328 if(c_pause)
329 {
330 snprintf(buffer, sizeof(buffer) - 1, "pause %d", c_pause);
331 bytes = write(s, buffer, strlen(buffer));
332 }
333
334 if(c_restart)
335 {
336 snprintf(buffer, sizeof(buffer) - 1, "restart %d", c_restart);
337 bytes = write(s, buffer, strlen(buffer));
338 }
339
340 /* requests for information */
341 if(c_load)
342 {
343 bytes = write(s, "load", 4);
344 receive(s, buffer, sizeof(buffer), TRUE);
345 }
346
347 if(c_status)
348 {
349 bytes = write(s, "status", 6);
350 receive(s, buffer, sizeof(buffer), TRUE);
351 }
352
353 if(c_agents)
354 {
355 bytes = write(s, "agents", 6);
356 receive(s, buffer, sizeof(buffer), TRUE);
357 }
358
359 return 0;
360 }
361
362 /* listen to the scheulder */
363 if(verbose)
364 interface_usage();
365 while(!closing)
366 {
367 /* prepare for read */
368 FD_ZERO(&fds);
369 FD_SET(s, &fds);
370 FD_SET(fileno(stdin), &fds);
371 memset(buffer, '\0', sizeof(buffer));
372 select(s + 1, &fds, NULL, NULL, NULL);
373
374 /* check the socket */
375 if(FD_ISSET(s, &fds))
376 closing = receive(s, buffer, sizeof(buffer), FALSE);
377
378 /* check stdin */
379 if(FD_ISSET(fileno(stdin), &fds))
380 {
381 if(read(fileno(stdin), buffer, sizeof(buffer)) == 0)
382 break;
383
384 if(strcmp(buffer, "help\n") == 0)
385 {
386 interface_usage();
387 continue;
388 }
389
390 response = (strncmp(buffer, "agents", 6) == 0 ||
391 strncmp(buffer, "status", 6) == 0 ||
392 strcmp (buffer, "verbose\n" ) == 0 ||
393 strcmp (buffer, "load\n" ) == 0) ?
394 FALSE : TRUE;
395
396 if((bytes = write(s, buffer, strlen(buffer) - 1)) != strlen(buffer) - 1)
397 {
398 printf("ERROR: couldn't write %lu bytes to socket\n", bytes);
399 closing = 1;
400 }
401 }
402 }
403
404 return 0;
405 }
406