FOSSology  4.4.0
Open Source License Compliance by Open Source Software
scheduler.c
Go to the documentation of this file.
1 /*
2  SPDX-FileCopyrightText: © 2010, 2011, 2012 Hewlett-Packard Development Company, L.P.
3 
4  SPDX-License-Identifier: GPL-2.0-only
5 */
11 /* local includes */
12 #include <libfossrepo.h>
13 #include <agent.h>
14 #include <database.h>
15 #include <event.h>
16 #include <host.h>
17 #include <interface.h>
18 #include <scheduler.h>
19 #include <fossconfig.h>
20 
21 /* std library includes */
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 /* unix system includes */
27 #include <dirent.h>
28 #include <fcntl.h>
29 #include <signal.h>
30 #include <sys/mman.h>
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 #include <unistd.h>
34 #include <pwd.h>
35 #include <grp.h>
36 
37 /* glib includes */
38 #include <glib.h>
39 #include <gio/gio.h>
40 
44 #define TEST_ERROR(error, ...) \
45  if(error) \
46  { \
47  log_printf("ERROR %s.%d: %s\n", \
48  __FILE__, __LINE__, error->message); \
49  log_printf("ERROR %s.%d: ", __FILE__, __LINE__); \
50  log_printf(__VA_ARGS__); \
51  log_printf("\n"); \
52  g_clear_error(&error); \
53  continue; \
54  }
55 
56 /* global flags */
57 int verbose = 0;
58 int closing = 0;
59 
60 GThread* main_thread;
61 
62 #define SELECT_DECLS(type, name, l_op, w_op, val) type CONF_##name = val;
64 #undef SELECT_DECLS
65 
66 /* ************************************************************************** */
67 /* **** signals and events ************************************************** */
68 /* ************************************************************************** */
69 
70 #define MASK_SIGCHLD (1 << 0)
71 #define MASK_SIGALRM (1 << 1)
72 #define MASK_SIGTERM (1 << 2)
73 #define MASK_SIGQUIT (1 << 3)
74 #define MASK_SIGHUP (1 << 4)
75 
76 int sigmask = 0;
77 
93 void scheduler_sig_handle(int signo)
94 {
95  /* Anywhere you see a "#if __GNUC__" the code is checking if GCC is the
96  * compiler. This is because the __sync... set of functions are the GCC
97  * version of atomics.
98  *
99  * This means that if you aren't compiling with GCC, you can have a race
100  * condition that results in a signal being lost during the
101  * signal_scheduler() function.
102  *
103  * What could happen:
104  * 1. signal_scheduler() reads value of sigmask
105  * 2. scheduler receives a SIG**** and sets the correct bit in sigmask
106  * 3. signal_scheduler() clears sigmask by setting it to 0
107  *
108  * In this set of events, a signal has been lost. If this is a sigchld this
109  * could be very bad as a job could never get marked as finished.
110  */
111  switch(signo)
112  {
113 #if __GNUC__
114  case SIGCHLD: __sync_fetch_and_or(&sigmask, MASK_SIGCHLD); break;
115  case SIGTERM: __sync_fetch_and_or(&sigmask, MASK_SIGTERM); break;
116  case SIGQUIT: __sync_fetch_and_or(&sigmask, MASK_SIGQUIT); break;
117  case SIGHUP: __sync_fetch_and_or(&sigmask, MASK_SIGHUP); break;
118 #else
119  case SIGCHLD: sigmask |= MASK_SIGCHLD; break;
120  case SIGALRM: sigmask |= MASK_SIGALRM; break;
121  case SIGTERM: sigmask |= MASK_SIGTERM; break;
122  case SIGQUIT: sigmask |= MASK_SIGQUIT; break;
123  case SIGHUP: sigmask |= MASK_SIGHUP ; break;
124 #endif
125  }
126 }
127 
143 {
144  // the last time an update was run
145  static time_t last_update = 0;
146 
147  // copy of the mask
148  guint mask;
149 
150  /* this will get sigmask and set it to 0 */
151 #if __GNUC__
152  mask = __sync_fetch_and_and(&sigmask, 0);
153 #else
154  mask = sigmask;
155  sigmask = 0;
156 #endif
157 
158  /* initialize last_update */
159  if(last_update == 0)
160  last_update = time(NULL);
161 
162  /* signal: SIGCHLD
163  *
164  * A SIGCHLD has been received since the last time signal_scheduler() was
165  * called. Get all agents that have finished since last this happened and
166  * create an event for each.
167  */
168  if(mask & MASK_SIGCHLD)
169  {
170  pid_t n; // the next pid that has died
171  pid_t* pass;
172  int status; // status returned by waitpit()
173 
174  /* get all of the dead children's pids */
175  while((n = waitpid(-1, &status, WNOHANG)) > 0)
176  {
177  V_SCHED("SIGNALS: received sigchld for pid %d\n", n);
178  pass = g_new0(pid_t, 2);
179  pass[0] = n;
180  pass[1] = status;
181  event_signal(agent_death_event, pass);
182  }
183  }
184 
185  /* signal: SIGTERM
186  *
187  * A SIGTERM has been received. simply set the closing flag to 1 so that the
188  * scheduler will gracefully shutdown as all the agents finish running.
189  */
190  if(mask & MASK_SIGTERM)
191  {
192  V_SCHED("SIGNALS: Scheduler received terminate signal, shutting down gracefully\n");
193  event_signal(scheduler_close_event, (void*)0);
194  }
195 
196  /* signal: SIGQUIT
197  *
198  * A SIGQUIT has been received. Queue a scheduler_close_event so that the
199  * scheduler will immediately stop running. This will cause all the agents to
200  * be forcefully killed.
201  */
202  if(mask & MASK_SIGQUIT)
203  {
204  V_SCHED("SIGNALS: Scheduler received quit signal, shutting down scheduler\n");
205  event_signal(scheduler_close_event, (void*)1);
206  }
207 
208  /* signal: SIGHUP
209  *
210  * A SIGHUP has been received. reload the configuration files for the
211  * scheduler. This will run here instead of being queued as an event.
212  */
213  if(mask & MASK_SIGHUP)
214  {
215  V_SCHED("SIGNALS: Scheduler received SGIHUP, reloading configuration data\n");
216  scheduler_config_event(scheduler, NULL);
217  }
218 
219  /* Finish by checking if an agent update needs to be performed.
220  *
221  * Every CONF_agent_update_interval, the agents and database should be
222  * updated. The agents need to be updated to check for dead and unresponsive
223  * agents. The database is updated to make sure that a new job hasn't been
224  * scheduled without the scheduler being informed.
225  */
226  if((time(NULL) - last_update) > CONF_agent_update_interval )
227  {
228  V_SPECIAL("SIGNALS: Performing agent and database update\n");
229  event_signal(agent_update_event, NULL);
230  event_signal(database_update_event, NULL);
231  last_update = time(NULL);
232  }
233 }
234 
235 /* ************************************************************************** */
236 /* **** The actual scheduler ************************************************ */
237 /* ************************************************************************** */
238 
250 {
251  scheduler_t* ret = g_new0(scheduler_t, 1);
252 
253  ret->process_name = NULL;
254  ret->s_pid = getpid();
255  ret->s_daemon = FALSE;
256  ret->s_startup = FALSE;
257  ret->s_pause = TRUE;
258 
259  ret->sysconfig = NULL;
260  ret->sysconfigdir = g_strdup(sysconfigdir);
261  ret->logdir = LOG_DIR;
262  ret->logcmdline = FALSE;
263  ret->main_log = log;
264  ret->host_queue = NULL;
265 
266  ret->i_created = FALSE;
267  ret->i_terminate = FALSE;
268  ret->i_port = 0;
269  ret->server = NULL;
270  ret->workers = NULL;
271  ret->cancel = NULL;
272 
273  ret->job_queue = g_sequence_new(NULL);
274 
275  ret->db_conn = NULL;
276  ret->host_url = NULL;
277  ret->email_subject = NULL;
278  ret->email_header = NULL;
279  ret->email_footer = NULL;
280  ret->email_command = NULL;
281 
282  /* This regex should find:
283  * 1. One or more capital letters followed by a ':' followed by white space,
284  * followed by a number
285  * 2. One or more capital letters followed by a ':' followed by white space,
286  * followed by a number, followed by white space, followed by a number
287  *
288  * Examples:
289  * HEART: 1 2 -> matches
290  * HEART: 1 -> matches
291  * HEART: -> does not match
292  *
293  */
294  ret->parse_agent_msg = g_regex_new(
295  "([A-Z]+):([ \t]+)(\\d+)(([ \t]+)(\\d))?",
296  0, 0, NULL);
297 
298  /* This regex should find:
299  * 1. A '$' followed by any combination of capital letters or underscore
300  * 2. A '$' followed by any combination of capital letters or underscore,
301  * followed by a '.' followed by alphabetic characters or underscore,
302  * followed by a '.' followed by alphabetic characters or underscore
303  *
304  * Examples:
305  * $HELLO -> matches
306  * $SIMPLE_NAME -> matches
307  * $DB.table.column -> matches
308  * $bad -> does not match
309  * $DB.table -> does not match
310  */
311  ret->parse_db_email = g_regex_new(
312  "\\$([A-Z_]*)(\\.([a-zA-Z_]*)\\.([a-zA-Z_]*))?",
313  0, 0, NULL);
314 
315  /* This regex should match:
316  * 1. a set of alphabetical characters
317  * 2. a set of alphabetical characters, followed by white space, followed by
318  * a number
319  * 3. a set of alphabetical characters, followed by white space, followed by
320  * a number, followed by white space, followed by a string in quotes.
321  *
322  *
323  * Examples:
324  * close -> matches
325  * stop -> matches
326  * pause 10 -> matches
327  * kill 10 "hello world" -> matches
328  * pause 10 10 -> does not match
329  * kill "hello world" 10 -> does not match
330  *
331  *
332  */
333  ret->parse_interface_cmd = g_regex_new(
334  "(\\w+)(\\s+(-?\\d+))?(\\s+((-?\\d+)|(\"(.*)\")))?",
335  0, G_REGEX_MATCH_NEWLINE_LF, NULL);
336 
337  ret->meta_agents = g_tree_new_full(string_compare, NULL, NULL,
338  (GDestroyNotify)meta_agent_destroy);
339  ret->agents = g_tree_new_full(int_compare, NULL, NULL,
340  (GDestroyNotify)agent_destroy);
341  ret->host_list = g_tree_new_full(string_compare, NULL, NULL,
342  (GDestroyNotify)host_destroy);
343  ret->job_list = g_tree_new_full(int_compare, NULL, NULL,
344  (GDestroyNotify)job_destroy);
345 
346  main_log = log;
347 
348  return ret;
349 }
350 
363 {
364 
366 
367  if(scheduler->main_log)
368  {
369  log_destroy(scheduler->main_log);
370  main_log = NULL;
371  }
372 
373  if(scheduler->process_name) g_free(scheduler->process_name);
374  if(scheduler->sysconfig) fo_config_free(scheduler->sysconfig);
375  if(scheduler->sysconfigdir) g_free(scheduler->sysconfigdir);
376  if(scheduler->host_queue) g_list_free(scheduler->host_queue);
377  if(scheduler->workers) g_thread_pool_free(scheduler->workers, FALSE, TRUE);
378 
379  if(scheduler->email_subject) g_free(scheduler->email_subject);
380  if(scheduler->email_command) g_free(scheduler->email_command);
381 
382  g_sequence_free(scheduler->job_queue);
383 
384  g_regex_unref(scheduler->parse_agent_msg);
385  g_regex_unref(scheduler->parse_db_email);
386  g_regex_unref(scheduler->parse_interface_cmd);
387 
388  g_tree_unref(scheduler->meta_agents);
389  g_tree_unref(scheduler->agents);
390  g_tree_unref(scheduler->host_list);
391  g_tree_unref(scheduler->job_list);
392 
393  if (scheduler->db_conn) PQfinish(scheduler->db_conn);
394 
395  g_free(scheduler);
396 }
397 
406 static gboolean isMaxLimitReached(meta_agent_t* agent)
407 {
408  if (agent != NULL && agent->max_run <= agent->run_count)
409  {
410  return TRUE;
411  }
412  else
413  {
414  return FALSE;
415  }
416 }
417 
434 {
435  /* queue used to hold jobs if an exclusive job enters the system */
436  static job_t* job = NULL;
437  static host_t* host = NULL;
438  static int lockout = 0;
439 
440  /* locals */
441  int n_agents = g_tree_nnodes(scheduler->agents);
442  int n_jobs = active_jobs(scheduler->job_list);
443 
444  /* check to see if we are in and can exit the startup state */
445  if(scheduler->s_startup && n_agents == 0)
446  {
447  event_signal(database_update_event, NULL);
448  scheduler->s_startup = 0;
449  }
450 
451  /* check if we are able to close the scheduler */
452  if(closing && n_agents == 0 && n_jobs == 0)
453  {
455  return;
456  }
457 
458  if(lockout && n_agents == 0 && n_jobs == 0)
459  lockout = 0;
460 
461  if(job == NULL && !lockout)
462  {
463  while((job = peek_job(scheduler->job_queue)) != NULL)
464  {
465  // Check the max limit of running agents
466  if (isMaxLimitReached(
467  g_tree_lookup(scheduler->meta_agents, job->agent_type)))
468  {
469  V_SCHED("JOB_INIT: Unable to run agent %s due to max_run limit.\n",
470  job->agent_type);
471  job = NULL;
472  break;
473  }
474  // check if the agent is required to run on local host
475  if(is_meta_special(
476  g_tree_lookup(scheduler->meta_agents, job->agent_type), SAG_LOCAL))
477  {
478  host = g_tree_lookup(scheduler->host_list, LOCAL_HOST);
479  if(!(host->running < host->max))
480  {
481  job = NULL;
482  break;
483  }
484  }
485  // check if the job is required to run on a specific machine
486  else if((job->required_host != NULL))
487  {
488  host = g_tree_lookup(scheduler->host_list, job->required_host);
489  if(host != NULL)
490  {
491  if(!(host->running < host->max))
492  {
493  job = NULL;
494  break;
495  }
496  } else {
497  //log_printf("ERROR %s.%d: jq_pk %d jq_host '%s' not in the agent list!\n",
498  // __FILE__, __LINE__, job->id, job->required_host);
499  job->message = "ERROR: jq_host not in the agent list!";
500  job_fail_event(scheduler, job);
501  job = NULL;
502  break;
503  }
504  }
505  // the generic case, this can run anywhere, find a place
506  else if((host = get_host(&(scheduler->host_queue), 1)) == NULL)
507  {
508  job = NULL;
509  break;
510  }
511 
512  next_job(scheduler->job_queue);
513  if(is_meta_special(
514  g_tree_lookup(scheduler->meta_agents, job->agent_type), SAG_EXCLUSIVE))
515  {
516  V_SCHED("JOB_INIT: exclusive, postponing initialization\n");
517  break;
518  }
519 
520  V_SCHED("Starting JOB[%d].%s\n", job->id, job->agent_type);
521  agent_init(scheduler, host, job);
522  job = NULL;
523  }
524  }
525 
526  if(job != NULL && n_agents == 0 && n_jobs == 0)
527  {
528  agent_init(scheduler, host, job);
529  lockout = 1;
530  job = NULL;
531  host = NULL;
532  }
533 
534  if(scheduler->s_pause)
535  {
536  scheduler->s_startup = 1;
537  scheduler->s_pause = 0;
538  }
539 }
540 
541 /* ************************************************************************** */
542 /* **** main utility functions ********************************************** */
543 /* ************************************************************************** */
544 
545 #define GU_HEADER "DIRECTORIES"
546 #define GU_GROUP "PROJECTGROUP"
547 #define GU_USER "PROJECTUSER"
548 
557 void set_usr_grp(gchar* process_name, fo_conf* config)
558 {
559  /* locals */
560  struct group* grp;
561  struct passwd* pwd;
562 
563  char* group =
564  fo_config_has_key(config, GU_HEADER, GU_GROUP) ?
565  fo_config_get (config, GU_HEADER, GU_GROUP, NULL) : PROJECT_GROUP;
566  char* user =
567  fo_config_has_key(config, GU_HEADER, GU_USER) ?
568  fo_config_get (config, GU_HEADER, GU_USER, NULL) : PROJECT_USER;
569 
570  /* make sure group exists */
571  grp = getgrnam(group);
572  if(!grp)
573  {
574  fprintf(stderr, "FATAL %s.%d: could not find group \"%s\"\n",
575  __FILE__, __LINE__, group);
576  fprintf(stderr, "FATAL set_usr_grp() aborting due to error: %s\n",
577  strerror(errno));
578  exit(-1);
579  }
580 
581  /* set the project group */
582  setgroups(1, &(grp->gr_gid));
583  if((setgid(grp->gr_gid) != 0) || (setegid(grp->gr_gid) != 0))
584  {
585  fprintf(stderr, "FATAL %s.%d: %s must be run as root or %s\n",
586  __FILE__, __LINE__, process_name, user);
587  fprintf(stderr, "FATAL Set group '%s' aborting due to error: %s\n",
588  group, strerror(errno));
589  exit(-1);
590  }
591 
592  /* run as project user */
593  pwd = getpwnam(user);
594  if(!pwd)
595  {
596  fprintf(stderr, "FATAL %s.%d: user '%s' not found\n",
597  __FILE__, __LINE__, user);
598  exit(-1);
599  }
600 
601  /* run as correct user, not as root or any other user */
602  if((setuid(pwd->pw_uid) != 0) || (seteuid(pwd->pw_uid) != 0))
603  {
604  fprintf(stderr, "FATAL %s.%d: %s must run this as %s\n",
605  __FILE__, __LINE__, process_name, user);
606  fprintf(stderr, "FATAL SETUID aborting due to error: %s\n",
607  strerror(errno));
608  exit(-1);
609  }
610 }
611 
620 int kill_scheduler(int force)
621 {
622  gchar f_name[FILENAME_MAX];
623  struct dirent* ep;
624  DIR* dp;
625  FILE* file;
626  gint num_killed = 0;
627  pid_t s_pid = getpid();
628 
629  if((dp = opendir("/proc/")) == NULL)
630  {
631  fprintf(stderr, "ERROR %s.%d: Could not open /proc/ file system\n",
632  __FILE__, __LINE__);
633  exit(-1);
634  }
635 
636  while((ep = readdir(dp)) != NULL)
637  {
638  if(string_is_num(ep->d_name))
639  {
640  snprintf(f_name, sizeof(f_name), "/proc/%s/cmdline", ep->d_name);
641  if((file = fopen(f_name, "rt")))
642  {
643  if(fgets(f_name, sizeof(f_name), file) != NULL &&
644  strstr(f_name, "fo_scheduler") && s_pid != atoi(ep->d_name))
645  {
646  NOTIFY("KILL: send signal to process %s\n", ep->d_name);
647  if(force)
648  kill(atoi(ep->d_name), SIGQUIT);
649  else
650  kill(atoi(ep->d_name), SIGTERM);
651  num_killed++;
652  }
653 
654  fclose(file);
655  }
656  }
657  }
658 
659  closedir(dp);
660 
661  if(num_killed == 0)
662  return -1;
663  return 0;
664 }
665 
672 {
673  g_tree_clear(scheduler->meta_agents);
674  g_tree_clear(scheduler->host_list);
675 
676  g_list_free(scheduler->host_queue);
677  scheduler->host_queue = NULL;
678 
679  g_free(scheduler->host_url);
680  g_free(scheduler->email_subject);
681  g_free(scheduler->email_command);
682  PQfinish(scheduler->db_conn);
683  scheduler->db_conn = NULL;
684  scheduler->host_url = NULL;
685  scheduler->email_subject = NULL;
686  scheduler->email_command = NULL;
687 
688  if(scheduler->default_header)
689  munmap(scheduler->email_header, strlen(scheduler->email_header));
690  if(scheduler->default_footer)
691  munmap(scheduler->email_footer, strlen(scheduler->email_footer));
692  scheduler->email_header = NULL;
693  scheduler->email_footer = NULL;
694 
695  fo_config_free(scheduler->sysconfig);
696  scheduler->sysconfig = NULL;
697 }
698 
707 static gboolean g_tree_collect(gpointer key, gpointer value, gpointer data)
708 {
709  GList** ret = (GList**)data;
710 
711  *ret = g_list_append(*ret, key);
712 
713  return 0;
714 }
715 
721 void g_tree_clear(GTree* tree)
722 {
723  GList* keys = NULL;
724  GList* iter = NULL;
725 
726  g_tree_foreach(tree, g_tree_collect, &keys);
727 
728  for(iter = keys; iter != NULL; iter = iter->next)
729  g_tree_remove(tree, iter->data);
730 
731  g_list_free(keys);
732 }
733 
744 {
745  DIR* dp; // directory pointer used to load meta agents;
746  struct dirent* ep; // information about directory
747  gchar* dirname; // holds the name of the current configuration file
748  uint8_t max = -1; // the number of agents to a host or number of one type running
749  uint32_t special = 0; // anything that is special about the agent (EXCLUSIVE)
750  int32_t i;
751  gchar* name;
752  gchar* cmd;
753  gchar* tmp;
754  GError* error = NULL;
755  fo_conf* config;
756 
757  dirname = g_strdup_printf("%s/%s/", scheduler->sysconfigdir, AGENT_CONF);
758  if((dp = opendir(dirname)) == NULL)
759  {
760  FATAL("Could not open agent config directory: %s", dirname);
761  return;
762  }
763  g_free(dirname);
764 
765  /* load the configuration for the agents */
766  while((ep = readdir(dp)) != NULL)
767  {
768  if(ep->d_name[0] != '.')
769  {
770  dirname = g_strdup_printf("%s/%s/%s/%s.conf",
771  scheduler->sysconfigdir, AGENT_CONF, ep->d_name, ep->d_name);
772 
773  config = fo_config_load(dirname, &error);
774  if(error && error->code == fo_missing_file)
775  {
776  V_SCHED("CONFIG: Could not find %s\n", dirname);
777  g_clear_error(&error);
778  continue;
779  }
780  TEST_ERROR(error, "no additional info");
781  V_SCHED("CONFIG: loading config file %s\n", dirname);
782 
783  if(!fo_config_has_group(config, "default"))
784  {
785  log_printf("ERROR: %s must have a \"default\" group\n", dirname);
786  log_printf("ERROR: cause by %s.%d\n", __FILE__, __LINE__);
787  continue;
788  }
789 
790  special = 0;
791  name = ep->d_name;
792  max = fo_config_list_length(config, "default", "special", &error);
793  TEST_ERROR(error, "%s: the special key should be of type list", dirname);
794  for(i = 0; i < max; i++)
795  {
796  cmd = fo_config_get_list(config, "default", "special", i, &error);
797  TEST_ERROR(error, "%s: failed to load element %d of special list",
798  dirname, i)
799 
800  if(cmd[0] != '\0') {
801  if(strncmp(cmd, "EXCLUSIVE", 9) == 0)
802  special |= SAG_EXCLUSIVE;
803  else if(strncmp(cmd, "NOEMAIL", 7) == 0)
804  special |= SAG_NOEMAIL;
805  else if(strncmp(cmd, "NOKILL", 6) == 0)
806  special |= SAG_NOKILL;
807  else if(strncmp(cmd, "LOCAL", 6) == 0)
808  special |= SAG_LOCAL;
809  else if(strlen(cmd) != 0)
810  WARNING("%s: Invalid special type for agent %s: %s",
811  dirname, name, cmd);
812  }
813  }
814 
815  cmd = fo_config_get(config, "default", "command", &error);
816  TEST_ERROR(error, "%s: the default group must have a command key", dirname);
817  tmp = fo_config_get(config, "default", "max", &error);
818  TEST_ERROR(error, "%s: the default group must have a max key", dirname);
819 
820  if(!add_meta_agent(scheduler->meta_agents, name, cmd, (max = atoi(tmp)), special))
821  {
822  V_SCHED("CONFIG: could not create meta agent using %s\n", ep->d_name);
823  }
824  else if(TVERB_SCHED)
825  {
826  log_printf("CONFIG: added new agent\n");
827  log_printf(" name = %s\n", name);
828  log_printf(" command = %s\n", cmd);
829  log_printf(" max = %d\n", max);
830  log_printf(" special = %d\n", special);
831  }
832 
833  g_free(dirname);
834  fo_config_free(config);
835  }
836  }
837 
838  closedir(dp);
839  event_signal(scheduler_test_agents, NULL);
840 }
841 
854 {
855  gchar* tmp; // pointer into a string
856  gchar** keys; // list of host names grabbed from the config file
857  int32_t max = -1; // the number of agents to a host or number of one type running
858  int32_t special = 0; // anything that is special about the agent (EXCLUSIVE)
859  gchar addbuf[512]; // standard string buffer
860  gchar dirbuf[FILENAME_MAX]; // standard string buffer
861  GError* error = NULL; // error return location
862  int32_t i; // indexing variable
863  host_t* host; // new hosts will be created in the loop
864  fo_conf* version; // information loaded from the version file
865 
866  if(scheduler->sysconfig != NULL)
867  fo_config_free(scheduler->sysconfig);
868 
869  /* parse the config file */
870  tmp = g_strdup_printf("%s/fossology.conf", scheduler->sysconfigdir);
871  scheduler->sysconfig = fo_config_load(tmp, &error);
872  if(error) FATAL("%s", error->message);
873  g_free(tmp);
874 
875  /* set the user and group before proceeding */
876  set_usr_grp(scheduler->process_name, scheduler->sysconfig);
877 
878  /* load the port setting */
879  if(scheduler->i_port == 0)
880  scheduler->i_port = atoi(fo_config_get(scheduler->sysconfig,
881  "FOSSOLOGY", "port", &error));
882 
883  /* load the log directory */
884  if(!scheduler->logcmdline)
885  {
886  if(fo_config_has_key(scheduler->sysconfig, "DIRECTORIES", "LOGDIR"))
887  scheduler->logdir = fo_config_get(scheduler->sysconfig, "DIRECTORIES", "LOGDIR", &error);
888  scheduler->main_log = log_new(scheduler->logdir, NULL, scheduler->s_pid);
889 
890  if(main_log)
891  {
893  main_log = scheduler->main_log;
894  }
895  }
896 
897  /* load the host settings */
898  keys = fo_config_key_set(scheduler->sysconfig, "HOSTS", &special);
899  for(i = 0; i < special; i++)
900  {
901  tmp = fo_config_get(scheduler->sysconfig, "HOSTS", keys[i], &error);
902  if(error)
903  {
904  WARNING("%s\n", error->message);
905  g_clear_error(&error);
906  continue;
907  }
908 
909  sscanf(tmp, "%s %s %d", addbuf, dirbuf, &max);
910  host = host_init(keys[i], addbuf, dirbuf, max);
911  host_insert(host, scheduler);
912  if(TVERB_SCHED)
913  {
914  log_printf("CONFIG: added new host\n");
915  log_printf(" name = %s\n", keys[i]);
916  log_printf(" address = %s\n", addbuf);
917  log_printf(" directory = %s\n", dirbuf);
918  log_printf(" max = %d\n", max);
919  }
920  }
921 
922  if((tmp = fo_RepValidate(scheduler->sysconfig)) != NULL)
923  {
924  ERROR("configuration file failed repository validation");
925  ERROR("The offending line: \"%s\"", tmp);
926  g_free(tmp);
927  exit(254);
928  }
929 
930  /* load the version information */
931  tmp = g_strdup_printf("%s/VERSION", scheduler->sysconfigdir);
932  version = fo_config_load(tmp, &error);
933  if(error) FATAL("%s", error->message);
934  g_free(tmp);
935 
936  fo_config_join(scheduler->sysconfig, version, NULL);
937  fo_config_free(version);
938 
939  /* This will create the load and the print command for the special
940  * configuration variables. This uses the l_op operation to load the variable
941  * from the file and the w_op variable to write the variable to the log file.
942  *
943  * example:
944  * if this is in the CONF_VARIABLES_TYPES():
945  *
946  * apply(char*, test_variable, NOOP, %s, "hello")
947  *
948  * this is generated:
949  *
950  * if(fo_config_has_key(sysconfig, "SCHEDULER", "test_variable")
951  * CONF_test_variable = fo_config_get(sysconfig, "SCHEDULER",
952  * "test_variable", NULL);
953  * V_SPECIAL("CONFIG: %s == %s\n", "test_variable", CONF_test_variable);
954  *
955  */
956 #define SELECT_CONF_INIT(type, name, l_op, w_op, val) \
957  if(fo_config_has_key(scheduler->sysconfig, "SCHEDULER", #name)) \
958  CONF_##name = l_op(fo_config_get(scheduler->sysconfig, "SCHEDULER", #name, NULL)); \
959  V_SPECIAL("CONFIG: %s == " MK_STRING_LIT(w_op) "\n", #name, CONF_##name );
960  CONF_VARIABLES_TYPES(SELECT_CONF_INIT)
961 #undef SELECT_CONF_INIT
962 }
963 
974 {
975  int ret = 0;
976 
977  /* daemonize the process */
978  if((ret = daemon(0, 0)) != 0)
979  return ret;
980 
981  scheduler->s_pid = getpid();
982  return ret;
983 }
984 
991 void scheduler_config_event(scheduler_t* scheduler, void* unused)
992 {
993  if(scheduler->sysconfig)
994  scheduler_clear_config(scheduler);
995 
996  scheduler_foss_config(scheduler);
997  scheduler_agent_config(scheduler);
998 
999  database_init(scheduler);
1000  email_init(scheduler);
1001 }
1002 
1014 void scheduler_close_event(scheduler_t* scheduler, void* killed)
1015 {
1016  closing = 1;
1017 
1018  if(killed) {
1019  kill_agents(scheduler);
1020  }
1021 }
1022 
1029 void scheduler_test_agents(scheduler_t* scheduler, void* unused)
1030 {
1031  scheduler->s_startup = TRUE;
1032  test_agents(scheduler);
1033 }
1034 
1041 gint string_is_num(gchar* str)
1042 {
1043  int len = strlen(str);
1044  int i;
1045 
1046  for(i = 0; i < len; i++)
1047  if(!isdigit(str[i]))
1048  return FALSE;
1049  return TRUE;
1050 }
1051 
1060 gint string_compare(gconstpointer a, gconstpointer b, gpointer user_data)
1061 {
1062  return strcmp((char*)a, (char*)b);
1063 }
1064 
1074 gint int_compare(gconstpointer a, gconstpointer b, gpointer user_data)
1075 {
1076  return *(int*)a - *(int*)b;
1077 }
agent_t * agent_init(scheduler_t *scheduler, host_t *host, job_t *job)
Allocate and spawn a new agent.
Definition: agent.c:802
void agent_update_event(scheduler_t *scheduler, void *unused)
Definition: agent.c:1087
void agent_destroy(agent_t *agent)
Frees the memory associated with an agent.
Definition: agent.c:924
int add_meta_agent(GTree *meta_agents, char *name, char *cmd, int max, int spc)
Definition: agent.c:1316
void test_agents(scheduler_t *scheduler)
Calls the agent test function for every type of agent.
Definition: agent.c:1290
void kill_agents(scheduler_t *scheduler)
Call the agent_kill function for every agent within the system.
Definition: agent.c:1301
int is_meta_special(meta_agent_t *ma, int special_type)
tests if a particular meta agent has a specific special flag set
Definition: agent.c:1341
void agent_death_event(scheduler_t *scheduler, pid_t *pid)
Definition: agent.c:952
void meta_agent_destroy(meta_agent_t *ma)
Definition: agent.c:784
Header file with agent related operations.
#define SAG_NOEMAIL
This agent should not send notification emails.
Definition: agent.h:35
#define SAG_NOKILL
This agent should not be killed when updating the agent.
Definition: agent.h:33
#define SAG_EXCLUSIVE
This agent must not run at the same time as any other agent.
Definition: agent.h:34
#define SAG_LOCAL
This agent should only run on localhost.
Definition: agent.h:36
void event_loop_terminate()
Stops the event loop from executing.
Definition: event.c:275
void event_loop_destroy()
Frees any memory associated with the event queue.
Definition: event.c:179
Event handling operations.
char * fo_config_get_list(fo_conf *conf, char *group, char *key, int idx, GError **error)
Definition: fossconfig.c:382
int fo_config_list_length(fo_conf *conf, char *group, char *key, GError **error)
Gets the length of the list associated with a particular list key.
Definition: fossconfig.c:475
void fo_config_free(fo_conf *conf)
Frees the memory associated with the internal configuration data structures.
Definition: fossconfig.c:506
fo_conf * fo_config_load(char *rawname, GError **error)
Load the configuration information from the provided file.
Definition: fossconfig.c:275
char * fo_config_get(fo_conf *conf, const char *group, const char *key, GError **error)
Gets an element based on its group name and key name. If the group or key is not found,...
Definition: fossconfig.c:336
int fo_config_has_group(fo_conf *conf, char *group)
Checks if the currently parsed configuration file has a specific group.
Definition: fossconfig.c:651
void fo_config_join(fo_conf *dst, fo_conf *src, GError **error)
Takes all groups and key from a fo_conf and adds them to another.
Definition: fossconfig.c:531
char ** fo_config_key_set(fo_conf *conf, char *group, int *length)
Gets the set of key names for a particular group.
Definition: fossconfig.c:614
int fo_config_has_key(fo_conf *conf, char *group, char *key)
Checks if the a specific group in the currently parsed configuration file has a specific key.
Definition: fossconfig.c:668
FOSSology library to read config file.
@ fo_missing_file
File is missing.
Definition: fossconfig.h:22
void host_insert(host_t *host, scheduler_t *scheduler)
Inserts a new host into the scheduler structure.
Definition: host.c:91
host_t * host_init(char *name, char *address, char *agent_dir, int max)
Creates a new host, and adds it to the host list.
Definition: host.c:48
void host_destroy(host_t *host)
Frees and uninitializes any memory associated with the host struct.
Definition: host.c:66
host_t * get_host(GList **queue, uint8_t num)
Definition: host.c:144
void job_fail_event(scheduler_t *scheduler, job_t *job)
Events that causes a job to be marked a failed.
Definition: job.c:406
job_t * next_job(GSequence *job_queue)
Gets the next job from the job queue.
Definition: job.c:681
job_t * peek_job(GSequence *job_queue)
Gets the job that is at the top of the queue if there is one.
Definition: job.c:701
void job_destroy(job_t *job)
Definition: job.c:201
uint32_t active_jobs(GTree *job_list)
Gets the number of jobs that are not paused.
Definition: job.c:720
FUNCTION int max(int permGroup, int permPublic)
Get the maximum group privilege.
Definition: libfossagent.c:295
char * fo_RepValidate(fo_conf *config)
validates the repository configuration information.
Definition: libfossrepo.c:977
char * sysconfigdir
log_t * main_log
Definition: logging.c:33
log_t * log_new(gchar *log_name, gchar *pro_name, pid_t pro_pid)
Creates a new log.
Definition: logging.c:81
void log_destroy(log_t *log)
Free memory associated with the log file.
Definition: logging.c:150
#define ERROR(...)
Definition: logging.h:79
#define FATAL(...)
Definition: logging.h:63
void database_init(scheduler_t *scheduler)
Definition: database.c:770
void database_update_event(scheduler_t *scheduler, void *unused)
Checks the job queue for any new entries.
Definition: database.c:841
void email_init(scheduler_t *scheduler)
Loads information about the email that will be sent for job notifications.
Definition: database.c:557
int verbose
The verbose level.
Definition: scheduler.c:57
void scheduler_config_event(scheduler_t *scheduler, void *unused)
Load both the fossology configuration and all the agent configurations.
Definition: scheduler.c:991
int closing
Set if scheduler is shutting down.
Definition: scheduler.c:58
void scheduler_agent_config(scheduler_t *scheduler)
Loads a particular agents configuration file.
Definition: scheduler.c:743
int scheduler_daemonize(scheduler_t *scheduler)
Daemonizes the scheduler.
Definition: scheduler.c:973
GThread * main_thread
Pointer to the main thread.
Definition: scheduler.c:60
gint string_is_num(gchar *str)
Checks if a string is entirely composed of numeric characters.
Definition: scheduler.c:1041
void g_tree_clear(GTree *tree)
Clears the contents of a GTree.
Definition: scheduler.c:721
static gboolean isMaxLimitReached(meta_agent_t *agent)
Check if the current agent's max limit is respected.
Definition: scheduler.c:406
void scheduler_close_event(scheduler_t *scheduler, void *killed)
Sets the closing flag and possibly kills all currently running agents.
Definition: scheduler.c:1014
void scheduler_test_agents(scheduler_t *scheduler, void *unused)
Event used when the scheduler tests the agents.
Definition: scheduler.c:1029
static gboolean g_tree_collect(gpointer key, gpointer value, gpointer data)
GTraverseFunc used by g_tree_clear to collect all the keys in a tree.
Definition: scheduler.c:707
void scheduler_update(scheduler_t *scheduler)
Update function called after every event.
Definition: scheduler.c:433
void scheduler_sig_handle(int signo)
Handles any signals sent to the scheduler that are not SIGCHLD.
Definition: scheduler.c:93
void scheduler_clear_config(scheduler_t *scheduler)
Clears any information that is loaded when loading the configuration.
Definition: scheduler.c:671
scheduler_t * scheduler_init(gchar *sysconfigdir, log_t *log)
Create a new scheduler object.
Definition: scheduler.c:249
gint int_compare(gconstpointer a, gconstpointer b, gpointer user_data)
Definition: scheduler.c:1074
void scheduler_foss_config(scheduler_t *scheduler)
Loads the configuration data from fossology.conf.
Definition: scheduler.c:853
gint string_compare(gconstpointer a, gconstpointer b, gpointer user_data)
Definition: scheduler.c:1060
void set_usr_grp(gchar *process_name, fo_conf *config)
Definition: scheduler.c:557
#define TEST_ERROR(error,...)
Definition: scheduler.c:44
int kill_scheduler(int force)
Kills all other running scheduler.
Definition: scheduler.c:620
void scheduler_destroy(scheduler_t *scheduler)
Free any memory associated with a scheduler_t.
Definition: scheduler.c:362
void scheduler_signal(scheduler_t *scheduler)
Function that handles certain signals being delivered to the scheduler.
Definition: scheduler.c:142
Header file for the scheduler.
#define SELECT_DECLS(type, name, l_op, w_op, val)
Definition: scheduler.h:253
#define CONF_VARIABLES_TYPES(apply)
Definition: scheduler.h:245
#define AGENT_CONF
Agent conf location.
Definition: scheduler.h:123
Definition: host.h:26
int running
The number of agents currently running on this host.
Definition: host.h:31
int max
The max number of agents that can run on this host.
Definition: host.h:30
The job structure.
Definition: job.h:51
int32_t id
The identifier for this job.
Definition: job.h:73
char * required_host
If not NULL, this job must run on a specific host machine.
Definition: job.h:54
gchar * message
Message that will be sent with job notification email.
Definition: job.h:69
char * agent_type
The type of agent used to analyze the data.
Definition: job.h:53
Definition: logging.h:35
int max_run
the maximum number that can run at once -1 if no limit
Definition: agent.h:85
int run_count
the count of agents in running state
Definition: agent.h:90
gboolean s_pause
Has the scheduler been paused.
Definition: scheduler.h:146
GThread * server
Thread that is listening to the server socket.
Definition: scheduler.h:167
GTree * job_list
List of jobs that have been created.
Definition: scheduler.h:172
gchar * email_header
The beginning of the email message.
Definition: scheduler.h:179
gchar * email_command
The command that will sends emails, usually mailx.
Definition: scheduler.h:181
GRegex * parse_interface_cmd
Parses the commands received by the interface.
Definition: scheduler.h:188
gchar * sysconfigdir
The system directory that contain fossology.conf.
Definition: scheduler.h:150
PGconn * db_conn
The database connection.
Definition: scheduler.h:176
gboolean s_startup
Has the scheduler finished startup tests.
Definition: scheduler.h:145
GList * host_queue
Round-robin queue for choosing which host use next.
Definition: scheduler.h:161
GTree * host_list
List of all hosts available to the scheduler.
Definition: scheduler.h:160
log_t * main_log
The main log file for the scheduler.
Definition: scheduler.h:153
gboolean s_pid
The pid of the scheduler process.
Definition: scheduler.h:143
GRegex * parse_agent_msg
Parses messages coming from the agents.
Definition: scheduler.h:186
gchar * logdir
The directory to put the log file in.
Definition: scheduler.h:151
GTree * meta_agents
List of all meta agents available to the scheduler.
Definition: scheduler.h:156
GThreadPool * workers
Threads to handle incoming network communication.
Definition: scheduler.h:168
gboolean default_footer
Is the footer the default footer.
Definition: scheduler.h:183
gchar * process_name
The name of the scheduler process.
Definition: scheduler.h:142
gboolean i_terminate
Has the interface been terminated.
Definition: scheduler.h:165
GTree * agents
List of any currently running agents.
Definition: scheduler.h:157
gboolean logcmdline
Was the log file set by the command line.
Definition: scheduler.h:152
gchar * email_subject
The subject to be used for emails.
Definition: scheduler.h:178
GCancellable * cancel
Used to stop the listening thread when it is running.
Definition: scheduler.h:169
GRegex * parse_db_email
Parses database email text.
Definition: scheduler.h:187
gboolean i_created
Has the interface been created.
Definition: scheduler.h:164
gchar * email_footer
The end of the email message.
Definition: scheduler.h:180
GSequence * job_queue
heap of jobs that still need to be started
Definition: scheduler.h:173
gboolean s_daemon
Is the scheduler being run as a daemon.
Definition: scheduler.h:144
gboolean default_header
Is the header the default header.
Definition: scheduler.h:182
gchar * host_url
The url that is used to get to the FOSSology instance.
Definition: scheduler.h:177
fo_conf * sysconfig
Configuration information loaded from the configuration file.
Definition: scheduler.h:149
uint16_t i_port
The port that the scheduler is listening on.
Definition: scheduler.h:166