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