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