FOSSology  4.7.1
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, 3); /* [0]=pid [1]=status [2]=retry */
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->scheduler_version = NULL;
274 
275  ret->job_queue = g_sequence_new(NULL);
276 
277  ret->db_conn = NULL;
278  ret->host_url = NULL;
279  ret->email_subject = NULL;
280  ret->email_header = NULL;
281  ret->email_footer = NULL;
282  ret->email_command = NULL;
283 
284  /* This regex should find:
285  * 1. One or more capital letters followed by a ':' followed by white space,
286  * followed by a number
287  * 2. One or more capital letters followed by a ':' followed by white space,
288  * followed by a number, followed by white space, followed by a number
289  *
290  * Examples:
291  * HEART: 1 2 -> matches
292  * HEART: 1 -> matches
293  * HEART: -> does not match
294  *
295  */
296  ret->parse_agent_msg = g_regex_new(
297  "([A-Z]+):([ \t]+)(\\d+)(([ \t]+)(\\d))?",
298  0, 0, NULL);
299 
300  /* This regex should find:
301  * 1. A '$' followed by any combination of capital letters or underscore
302  * 2. A '$' followed by any combination of capital letters or underscore,
303  * followed by a '.' followed by alphabetic characters or underscore,
304  * followed by a '.' followed by alphabetic characters or underscore
305  *
306  * Examples:
307  * $HELLO -> matches
308  * $SIMPLE_NAME -> matches
309  * $DB.table.column -> matches
310  * $bad -> does not match
311  * $DB.table -> does not match
312  */
313  ret->parse_db_email = g_regex_new(
314  "\\$([A-Z_]*)(\\.([a-zA-Z_]*)\\.([a-zA-Z_]*))?",
315  0, 0, NULL);
316 
317  /* This regex should match:
318  * 1. a set of alphabetical characters
319  * 2. a set of alphabetical characters, followed by white space, followed by
320  * a number
321  * 3. a set of alphabetical characters, followed by white space, followed by
322  * a number, followed by white space, followed by a string in quotes.
323  *
324  *
325  * Examples:
326  * close -> matches
327  * stop -> matches
328  * pause 10 -> matches
329  * kill 10 "hello world" -> matches
330  * pause 10 10 -> does not match
331  * kill "hello world" 10 -> does not match
332  *
333  *
334  */
335  ret->parse_interface_cmd = g_regex_new(
336  "(\\w+)(\\s+(-?\\d+))?(\\s+((-?\\d+)|(\"(.*)\")))?",
337  0, G_REGEX_MATCH_NEWLINE_LF, NULL);
338 
339  ret->meta_agents = g_tree_new_full(string_compare, NULL, NULL,
340  (GDestroyNotify)meta_agent_destroy);
341  ret->agents = g_tree_new_full(int_compare, NULL, NULL,
342  (GDestroyNotify)agent_destroy);
343  ret->host_list = g_tree_new_full(string_compare, NULL, NULL,
344  (GDestroyNotify)host_destroy);
345  ret->job_list = g_tree_new_full(int_compare, NULL, NULL,
346  (GDestroyNotify)job_destroy);
347 
348  main_log = log;
349 
350  return ret;
351 }
352 
365 {
366 
368 
369  if(scheduler->main_log)
370  {
371  log_destroy(scheduler->main_log);
372  main_log = NULL;
373  }
374 
375  if(scheduler->scheduler_version) g_free(scheduler->scheduler_version);
376  if(scheduler->process_name) g_free(scheduler->process_name);
377  if(scheduler->sysconfig) fo_config_free(scheduler->sysconfig);
378  if(scheduler->sysconfigdir) g_free(scheduler->sysconfigdir);
379  if(scheduler->host_queue) g_list_free(scheduler->host_queue);
380  if(scheduler->workers) g_thread_pool_free(scheduler->workers, FALSE, TRUE);
381 
382  if(scheduler->email_subject) g_free(scheduler->email_subject);
383  if(scheduler->email_command) g_free(scheduler->email_command);
384 
385  g_sequence_free(scheduler->job_queue);
386 
387  g_regex_unref(scheduler->parse_agent_msg);
388  g_regex_unref(scheduler->parse_db_email);
389  g_regex_unref(scheduler->parse_interface_cmd);
390 
391  g_tree_unref(scheduler->meta_agents);
392  g_tree_unref(scheduler->agents);
393  g_tree_unref(scheduler->host_list);
394  g_tree_unref(scheduler->job_list);
395 
396  if (scheduler->db_conn) PQfinish(scheduler->db_conn);
397 
398  g_free(scheduler);
399 }
400 
409 static gboolean isMaxLimitReached(meta_agent_t* agent)
410 {
411  // max_run < 0 means unlimited; only enforce a non-negative limit.
412  if (agent != NULL && agent->max_run >= 0 && agent->max_run <= agent->run_count)
413  {
414  return TRUE;
415  }
416  return FALSE;
417 }
418 
419 /* ************************************************************************** */
420 /* **** Stale CHECKEDOUT job reaper ***************************************** */
421 /* ************************************************************************** */
422 
427 typedef struct {
428  GList* list;
429  time_t now;
430 } stale_ctx;
431 
444 static gboolean collect_stale_jobs(gpointer key, gpointer val, gpointer data)
445 {
446  job_t* j = (job_t*)val;
447  stale_ctx* ctx = (stale_ctx*)data;
448 
449  // Skip non-CHECKEDOUT jobs and internal startup test jobs (id < 0).
450  if(j == NULL || j->status != JB_CHECKEDOUT || j->id < 0)
451  {
452  return FALSE;
453  }
454 
455  // Not stale if it still has agents assigned.
456  if(j->running_agents != NULL ||
457  j->finished_agents != NULL ||
458  j->failed_agents != NULL)
459  {
460  return FALSE;
461  }
462 
463  // Grace period: only flag as stale after CONF_agent_update_interval seconds beyond checkout time.
464  if((ctx->now - j->checkedout_at) <= CONF_agent_update_interval)
465  {
466  return FALSE;
467  }
468 
469  ctx->list = g_list_prepend(ctx->list, j);
470  return FALSE;
471 }
472 
482 static void reap_stale_jobs(scheduler_t* scheduler)
483 {
484  static time_t last_stale_check = 0;
485  time_t now = time(NULL);
486 
487  // Never reap during shutdown: CHECKEDOUT-with-no-agents jobs are legitimately in-flight, not stale.
488  if(closing)
489  {
490  return;
491  }
492 
493  // Rate-limit: bail immediately if the check interval hasn't elapsed.
494  if((now - last_stale_check) < CONF_agent_update_interval)
495  {
496  return;
497  }
498 
499  last_stale_check = now;
500 
501  // Skip the tree walk entirely when there are no jobs.
502  if(g_tree_nnodes(scheduler->job_list) == 0)
503  {
504  return;
505  }
506 
507  stale_ctx ctx = { NULL, now };
508  g_tree_foreach(scheduler->job_list, collect_stale_jobs, &ctx);
509 
510  // Nothing stale - common case, exit fast.
511  if(ctx.list == NULL)
512  {
513  return;
514  }
515 
516  for(GList* iter = ctx.list; iter != NULL; iter = iter->next)
517  {
518  job_t* j = (job_t*)iter->data;
519 
520  log_printf("WARNING %s.%d: JOB[%d] type:%s is CHECKEDOUT with no agents"
521  " - failing as stale\n",
522  __FILE__, __LINE__, j->id, j->agent_type);
523 
524  g_free(j->message);
525  j->message = g_strdup("job checked out but no agent was ever started (stale)");
526  job_fail_event(scheduler, j);
527 
528  // Remove from job_queue so the scheduler doesn't try to assign a fresh agent to this FAILED job.
529  {
530  GSequenceIter* sit = g_sequence_get_begin_iter(scheduler->job_queue);
531  while(!g_sequence_iter_is_end(sit))
532  {
533  if(g_sequence_get(sit) == j)
534  {
535  g_sequence_remove(sit);
536  break;
537  }
538  sit = g_sequence_iter_next(sit);
539  }
540  }
541 
542  /* no agents were spawned, so this removes the job from job_list and frees it */
543  job_remove_agent(j, scheduler->job_list, NULL);
544  }
545 
546  g_list_free(ctx.list);
547 }
548 
565 {
566  /* queue used to hold jobs if an exclusive job enters the system */
567  static job_t* job = NULL;
568  static host_t* host = NULL;
569  static int lockout = 0;
570 
571  /* locals */
572  int n_agents = g_tree_nnodes(scheduler->agents);
573  int n_jobs = active_jobs(scheduler->job_list);
574 
575  /* check to see if we are in and can exit the startup state */
576  if(scheduler->s_startup && n_agents == 0)
577  {
578  event_signal(database_update_event, NULL);
579  scheduler->s_startup = 0;
580  }
581 
582  /* check if we are able to close the scheduler */
583  if(closing && n_agents == 0 && n_jobs == 0)
584  {
586  return;
587  }
588 
589  if(lockout && n_agents == 0 && n_jobs == 0)
590  lockout = 0;
591 
592  if(job == NULL && !lockout)
593  {
594  /* blocked jobs are set aside in skipped_jobs and re-inserted (sorted) after
595  * the loop, so a blocked high-priority job doesn't starve the rest */
596  GList* skipped_jobs = NULL;
597 
598  while((job = peek_job(scheduler->job_queue)) != NULL)
599  {
600  // Check the max limit of running agents
601  if (isMaxLimitReached(
602  g_tree_lookup(scheduler->meta_agents, job->agent_type)))
603  {
604  V_SCHED("JOB_INIT: Unable to run agent %s due to max_run limit.\n",
605  job->agent_type);
606  // max_run reached: reset stale timer and defer to next cycle.
607  job->checkedout_at = time(NULL);
608  next_job(scheduler->job_queue);
609  skipped_jobs = g_list_prepend(skipped_jobs, job);
610  job = NULL;
611  continue;
612  }
613  // check if the agent is required to run on local host
614  if(is_meta_special(
615  g_tree_lookup(scheduler->meta_agents, job->agent_type), SAG_LOCAL))
616  {
617  host = g_tree_lookup(scheduler->host_list, LOCAL_HOST);
618  if(!(host->running < host->max))
619  {
620  // Local host full: reset stale timer and defer to next cycle.
621  job->checkedout_at = time(NULL);
622  next_job(scheduler->job_queue);
623  skipped_jobs = g_list_prepend(skipped_jobs, job);
624  job = NULL;
625  continue;
626  }
627  }
628  // check if the job is required to run on a specific machine
629  else if((job->required_host != NULL))
630  {
631  host = g_tree_lookup(scheduler->host_list, job->required_host);
632  if(host != NULL)
633  {
634  if(!(host->running < host->max))
635  {
636  // Required host full: reset stale timer and defer to next cycle.
637  job->checkedout_at = time(NULL);
638  next_job(scheduler->job_queue);
639  skipped_jobs = g_list_prepend(skipped_jobs, job);
640  job = NULL;
641  continue;
642  }
643  } else {
644  g_free(job->message);
645  job->message = g_strdup("ERROR: jq_host not in the agent list!");
646  job_fail_event(scheduler, job);
647  // Pop from queue first so job is not in queue when job_remove_agent frees it.
648  next_job(scheduler->job_queue);
649  job_remove_agent(job, scheduler->job_list, NULL);
650  job = NULL;
651  continue;
652  }
653  }
654  // the generic case, this can run anywhere, find a place
655  else if((host = get_host(&(scheduler->host_queue), 1)) == NULL)
656  {
657  // No host available: refresh stale timer on every queued job so reap_stale_jobs()
658  // doesn't misidentify legitimately blocked jobs as orphans.
659  time_t now = time(NULL);
660  GSequenceIter* sit = g_sequence_get_begin_iter(scheduler->job_queue);
661  while(!g_sequence_iter_is_end(sit))
662  {
663  ((job_t*)g_sequence_get(sit))->checkedout_at = now;
664  sit = g_sequence_iter_next(sit);
665  }
666  job = NULL;
667  break;
668  }
669 
670  next_job(scheduler->job_queue);
671  // Reset stale timer: job is now actively being dispatched.
672  job->checkedout_at = time(NULL);
673  if(is_meta_special(
674  g_tree_lookup(scheduler->meta_agents, job->agent_type), SAG_EXCLUSIVE))
675  {
676  V_SCHED("JOB_INIT: exclusive, postponing initialization\n");
677  break;
678  }
679 
680  V_SCHED("Starting JOB[%d].%s\n", job->id, job->agent_type);
681  agent_init(scheduler, host, job);
682  job = NULL;
683  }
684 
685  // Restore skipped jobs to the queue in priority order.
686  for(GList* sl = skipped_jobs; sl != NULL; sl = sl->next)
687  {
688  g_sequence_insert_sorted(scheduler->job_queue, sl->data, job_compare, NULL);
689  }
690  g_list_free(skipped_jobs);
691  }
692 
693  if(job != NULL && n_agents == 0 && n_jobs == 0)
694  {
695  agent_init(scheduler, host, job);
696  lockout = 1;
697  job = NULL;
698  host = NULL;
699  }
700 
701  // Exclusive job is held in the static `job` pointer; reset its stale timer so the reaper doesn't free it.
702  if(job != NULL)
703  {
704  job->checkedout_at = time(NULL);
705  }
706 
707  reap_stale_jobs(scheduler);
708 
709  if(scheduler->s_pause)
710  {
711  scheduler->s_startup = 1;
712  scheduler->s_pause = 0;
713  }
714 }
715 
716 /* ************************************************************************** */
717 /* **** main utility functions ********************************************** */
718 /* ************************************************************************** */
719 
720 #define GU_HEADER "DIRECTORIES"
721 #define GU_GROUP "PROJECTGROUP"
722 #define GU_USER "PROJECTUSER"
723 
732 void set_usr_grp(gchar* process_name, fo_conf* config)
733 {
734  /* locals */
735  struct group* grp;
736  struct passwd* pwd;
737 
738  char* group =
739  fo_config_has_key(config, GU_HEADER, GU_GROUP) ?
740  fo_config_get (config, GU_HEADER, GU_GROUP, NULL) : PROJECT_GROUP;
741  char* user =
742  fo_config_has_key(config, GU_HEADER, GU_USER) ?
743  fo_config_get (config, GU_HEADER, GU_USER, NULL) : PROJECT_USER;
744 
745  /* make sure group exists */
746  grp = getgrnam(group);
747  if(!grp)
748  {
749  fprintf(stderr, "FATAL %s.%d: could not find group \"%s\"\n",
750  __FILE__, __LINE__, group);
751  fprintf(stderr, "FATAL set_usr_grp() aborting due to error: %s\n",
752  strerror(errno));
753  exit(-1);
754  }
755 
756  /* set the project group */
757  setgroups(1, &(grp->gr_gid));
758  if((setgid(grp->gr_gid) != 0) || (setegid(grp->gr_gid) != 0))
759  {
760  fprintf(stderr, "FATAL %s.%d: %s must be run as root or %s\n",
761  __FILE__, __LINE__, process_name, user);
762  fprintf(stderr, "FATAL Set group '%s' aborting due to error: %s\n",
763  group, strerror(errno));
764  exit(-1);
765  }
766 
767  /* run as project user */
768  pwd = getpwnam(user);
769  if(!pwd)
770  {
771  fprintf(stderr, "FATAL %s.%d: user '%s' not found\n",
772  __FILE__, __LINE__, user);
773  exit(-1);
774  }
775 
776  /* run as correct user, not as root or any other user */
777  if((setuid(pwd->pw_uid) != 0) || (seteuid(pwd->pw_uid) != 0))
778  {
779  fprintf(stderr, "FATAL %s.%d: %s must run this as %s\n",
780  __FILE__, __LINE__, process_name, user);
781  fprintf(stderr, "FATAL SETUID aborting due to error: %s\n",
782  strerror(errno));
783  exit(-1);
784  }
785 }
786 
795 int kill_scheduler(int force)
796 {
797  gchar f_name[FILENAME_MAX];
798  struct dirent* ep;
799  DIR* dp;
800  FILE* file;
801  gint num_killed = 0;
802  pid_t s_pid = getpid();
803 
804  if((dp = opendir("/proc/")) == NULL)
805  {
806  fprintf(stderr, "ERROR %s.%d: Could not open /proc/ file system\n",
807  __FILE__, __LINE__);
808  exit(-1);
809  }
810 
811  while((ep = readdir(dp)) != NULL)
812  {
813  if(string_is_num(ep->d_name))
814  {
815  snprintf(f_name, sizeof(f_name), "/proc/%s/cmdline", ep->d_name);
816  if((file = fopen(f_name, "rt")))
817  {
818  if(fgets(f_name, sizeof(f_name), file) != NULL &&
819  strstr(f_name, "fo_scheduler") && s_pid != atoi(ep->d_name))
820  {
821  NOTIFY("KILL: send signal to process %s\n", ep->d_name);
822  if(force)
823  kill(atoi(ep->d_name), SIGQUIT);
824  else
825  kill(atoi(ep->d_name), SIGTERM);
826  num_killed++;
827  }
828 
829  fclose(file);
830  }
831  }
832  }
833 
834  closedir(dp);
835 
836  if(num_killed == 0)
837  return -1;
838  return 0;
839 }
840 
847 {
848  g_tree_clear(scheduler->meta_agents);
849  g_tree_clear(scheduler->host_list);
850 
851  g_list_free(scheduler->host_queue);
852  scheduler->host_queue = NULL;
853 
854  g_free(scheduler->host_url);
855  g_free(scheduler->email_subject);
856  g_free(scheduler->email_command);
857  PQfinish(scheduler->db_conn);
858  scheduler->db_conn = NULL;
859  scheduler->host_url = NULL;
860  scheduler->email_subject = NULL;
861  scheduler->email_command = NULL;
862 
863  if(scheduler->default_header)
864  munmap(scheduler->email_header, strlen(scheduler->email_header));
865  if(scheduler->default_footer)
866  munmap(scheduler->email_footer, strlen(scheduler->email_footer));
867  scheduler->email_header = NULL;
868  scheduler->email_footer = NULL;
869 
870  fo_config_free(scheduler->sysconfig);
871  scheduler->sysconfig = NULL;
872 
873  /* keep scheduler_version: scheduler_foss_config() compares it against the
874  * re-read COMMIT_HASH to detect a version change */
875 }
876 
885 static gboolean g_tree_collect(gpointer key, gpointer value, gpointer data)
886 {
887  GList** ret = (GList**)data;
888 
889  *ret = g_list_append(*ret, key);
890 
891  return 0;
892 }
893 
899 void g_tree_clear(GTree* tree)
900 {
901  GList* keys = NULL;
902  GList* iter = NULL;
903 
904  g_tree_foreach(tree, g_tree_collect, &keys);
905 
906  for(iter = keys; iter != NULL; iter = iter->next)
907  g_tree_remove(tree, iter->data);
908 
909  g_list_free(keys);
910 }
911 
922 {
923  DIR* dp; // directory pointer used to load meta agents;
924  struct dirent* ep; // information about directory
925  gchar* dirname; // holds the name of the current configuration file
926  uint8_t max = -1; // the number of agents to a host or number of one type running
927  uint32_t special = 0; // anything that is special about the agent (EXCLUSIVE)
928  int32_t i;
929  gchar* name;
930  gchar* cmd;
931  gchar* tmp;
932  GError* error = NULL;
933  fo_conf* config;
934 
935  dirname = g_strdup_printf("%s/%s/", scheduler->sysconfigdir, AGENT_CONF);
936  if((dp = opendir(dirname)) == NULL)
937  {
938  FATAL("Could not open agent config directory: %s", dirname);
939  return;
940  }
941  g_free(dirname);
942 
943  /* load the configuration for the agents */
944  while((ep = readdir(dp)) != NULL)
945  {
946  if(ep->d_name[0] != '.')
947  {
948  dirname = g_strdup_printf("%s/%s/%s/%s.conf",
949  scheduler->sysconfigdir, AGENT_CONF, ep->d_name, ep->d_name);
950 
951  config = fo_config_load(dirname, &error);
952  if(error && error->code == fo_missing_file)
953  {
954  V_SCHED("CONFIG: Could not find %s\n", dirname);
955  g_clear_error(&error);
956  continue;
957  }
958  TEST_ERROR(error, "no additional info");
959  V_SCHED("CONFIG: loading config file %s\n", dirname);
960 
961  if(!fo_config_has_group(config, "default"))
962  {
963  log_printf("ERROR: %s must have a \"default\" group\n", dirname);
964  log_printf("ERROR: cause by %s.%d\n", __FILE__, __LINE__);
965  continue;
966  }
967 
968  special = 0;
969  name = ep->d_name;
970  max = fo_config_list_length(config, "default", "special", &error);
971  TEST_ERROR(error, "%s: the special key should be of type list", dirname);
972  for(i = 0; i < max; i++)
973  {
974  cmd = fo_config_get_list(config, "default", "special", i, &error);
975  TEST_ERROR(error, "%s: failed to load element %d of special list",
976  dirname, i)
977 
978  if(cmd[0] != '\0') {
979  if(strncmp(cmd, "EXCLUSIVE", 9) == 0)
980  special |= SAG_EXCLUSIVE;
981  else if(strncmp(cmd, "NOEMAIL", 7) == 0)
982  special |= SAG_NOEMAIL;
983  else if(strncmp(cmd, "NOKILL", 6) == 0)
984  special |= SAG_NOKILL;
985  else if(strncmp(cmd, "LOCAL", 6) == 0)
986  special |= SAG_LOCAL;
987  else if(strlen(cmd) != 0)
988  WARNING("%s: Invalid special type for agent %s: %s",
989  dirname, name, cmd);
990  }
991  }
992 
993  cmd = fo_config_get(config, "default", "command", &error);
994  TEST_ERROR(error, "%s: the default group must have a command key", dirname);
995  tmp = fo_config_get(config, "default", "max", &error);
996  TEST_ERROR(error, "%s: the default group must have a max key", dirname);
997 
998  if(!add_meta_agent(scheduler->meta_agents, name, cmd, (max = atoi(tmp)), special))
999  {
1000  V_SCHED("CONFIG: could not create meta agent using %s\n", ep->d_name);
1001  }
1002  else if(TVERB_SCHED)
1003  {
1004  log_printf("CONFIG: added new agent\n");
1005  log_printf(" name = %s\n", name);
1006  log_printf(" command = %s\n", cmd);
1007  log_printf(" max = %d\n", max);
1008  log_printf(" special = %d\n", special);
1009  }
1010 
1011  g_free(dirname);
1012  fo_config_free(config);
1013  }
1014  }
1015 
1016  closedir(dp);
1017  event_signal(scheduler_test_agents, NULL);
1018 }
1019 
1032 {
1033  gchar* tmp; // pointer into a string
1034  gchar** keys; // list of host names grabbed from the config file
1035  int32_t max = -1; // the number of agents to a host or number of one type running
1036  int32_t special = 0; // anything that is special about the agent (EXCLUSIVE)
1037  gchar addbuf[512]; // standard string buffer
1038  gchar dirbuf[FILENAME_MAX]; // standard string buffer
1039  GError* error = NULL; // error return location
1040  int32_t i; // indexing variable
1041  host_t* host; // new hosts will be created in the loop
1042  fo_conf* version; // information loaded from the version file
1043 
1044  if(scheduler->sysconfig != NULL)
1045  fo_config_free(scheduler->sysconfig);
1046 
1047  /* parse the config file */
1048  tmp = g_strdup_printf("%s/fossology.conf", scheduler->sysconfigdir);
1049  scheduler->sysconfig = fo_config_load(tmp, &error);
1050  if(error) FATAL("%s", error->message);
1051  g_free(tmp);
1052 
1053  /* set the user and group before proceeding */
1054  set_usr_grp(scheduler->process_name, scheduler->sysconfig);
1055 
1056  /* load the port setting */
1057  if(scheduler->i_port == 0)
1058  scheduler->i_port = atoi(fo_config_get(scheduler->sysconfig,
1059  "FOSSOLOGY", "port", &error));
1060 
1061  /* load the log directory */
1062  if(!scheduler->logcmdline)
1063  {
1064  if(fo_config_has_key(scheduler->sysconfig, "DIRECTORIES", "LOGDIR"))
1065  scheduler->logdir = fo_config_get(scheduler->sysconfig, "DIRECTORIES", "LOGDIR", &error);
1066  scheduler->main_log = log_new(scheduler->logdir, NULL, scheduler->s_pid);
1067 
1068  if(main_log)
1069  {
1071  main_log = scheduler->main_log;
1072  }
1073  }
1074 
1075  /* load the host settings */
1076  keys = fo_config_key_set(scheduler->sysconfig, "HOSTS", &special);
1077  for(i = 0; i < special; i++)
1078  {
1079  tmp = fo_config_get(scheduler->sysconfig, "HOSTS", keys[i], &error);
1080  if(error)
1081  {
1082  WARNING("%s\n", error->message);
1083  g_clear_error(&error);
1084  continue;
1085  }
1086 
1087  sscanf(tmp, "%s %s %d", addbuf, dirbuf, &max);
1088  host = host_init(keys[i], addbuf, dirbuf, max);
1089  host_insert(host, scheduler);
1090  if(TVERB_SCHED)
1091  {
1092  log_printf("CONFIG: added new host\n");
1093  log_printf(" name = %s\n", keys[i]);
1094  log_printf(" address = %s\n", addbuf);
1095  log_printf(" directory = %s\n", dirbuf);
1096  log_printf(" max = %d\n", max);
1097  }
1098  }
1099 
1100  if((tmp = fo_RepValidate(scheduler->sysconfig)) != NULL)
1101  {
1102  ERROR("configuration file failed repository validation");
1103  ERROR("The offending line: \"%s\"", tmp);
1104  g_free(tmp);
1105  exit(254);
1106  }
1107 
1108  /* load the version information */
1109  tmp = g_strdup_printf("%s/VERSION", scheduler->sysconfigdir);
1110  version = fo_config_load(tmp, &error);
1111  if(error) FATAL("%s", error->message);
1112  g_free(tmp);
1113 
1114  fo_config_join(scheduler->sysconfig, version, NULL);
1115 
1116  /* read COMMIT_HASH so we can detect a rebuilt binary on the next SIGHUP */
1117  {
1118  GError* ver_err = NULL;
1119  const gchar* new_ver = fo_config_get(version, "BUILD", "COMMIT_HASH", &ver_err);
1120  if (ver_err)
1121  {
1122  g_clear_error(&ver_err);
1123  new_ver = "unknown";
1124  }
1125  if (scheduler->scheduler_version == NULL)
1126  {
1127  /* first load: just store it */
1128  scheduler->scheduler_version = g_strdup(new_ver);
1129  log_printf("NOTE: scheduler version initialised to \"%s\"\n",
1130  scheduler->scheduler_version);
1131  }
1132  else if (strcmp(scheduler->scheduler_version, new_ver) != 0)
1133  {
1134  /* version changed: store it and respawn agents on the new binary */
1135  log_printf("NOTE: scheduler version changed: \"%s\" -> \"%s\"\n",
1136  scheduler->scheduler_version, new_ver);
1137  g_free(scheduler->scheduler_version);
1138  scheduler->scheduler_version = g_strdup(new_ver);
1139  event_signal(scheduler_version_refresh, NULL);
1140  }
1141  }
1142 
1143  fo_config_free(version);
1144 
1145  /* This will create the load and the print command for the special
1146  * configuration variables. This uses the l_op operation to load the variable
1147  * from the file and the w_op variable to write the variable to the log file.
1148  *
1149  * example:
1150  * if this is in the CONF_VARIABLES_TYPES():
1151  *
1152  * apply(char*, test_variable, NOOP, %s, "hello")
1153  *
1154  * this is generated:
1155  *
1156  * if(fo_config_has_key(sysconfig, "SCHEDULER", "test_variable")
1157  * CONF_test_variable = fo_config_get(sysconfig, "SCHEDULER",
1158  * "test_variable", NULL);
1159  * V_SPECIAL("CONFIG: %s == %s\n", "test_variable", CONF_test_variable);
1160  *
1161  */
1162 #define SELECT_CONF_INIT(type, name, l_op, w_op, val) \
1163  if(fo_config_has_key(scheduler->sysconfig, "SCHEDULER", #name)) \
1164  CONF_##name = l_op(fo_config_get(scheduler->sysconfig, "SCHEDULER", #name, NULL)); \
1165  V_SPECIAL("CONFIG: %s == " MK_STRING_LIT(w_op) "\n", #name, CONF_##name );
1166  CONF_VARIABLES_TYPES(SELECT_CONF_INIT)
1167 #undef SELECT_CONF_INIT
1168 }
1169 
1180 {
1181  int ret = 0;
1182 
1183  /* daemonize the process */
1184  if((ret = daemon(0, 0)) != 0)
1185  return ret;
1186 
1187  scheduler->s_pid = getpid();
1188  return ret;
1189 }
1190 
1197 void scheduler_config_event(scheduler_t* scheduler, void* unused)
1198 {
1199  if(scheduler->sysconfig)
1200  scheduler_clear_config(scheduler);
1201 
1202  scheduler_foss_config(scheduler);
1203  scheduler_agent_config(scheduler);
1204 
1205  database_init(scheduler);
1206  email_init(scheduler);
1207 }
1208 
1220 void scheduler_close_event(scheduler_t* scheduler, void* killed)
1221 {
1222  closing = 1;
1223 
1224  if(killed) {
1225  kill_agents(scheduler);
1226  }
1227 }
1228 
1235 void scheduler_test_agents(scheduler_t* scheduler, void* unused)
1236 {
1237  scheduler->s_startup = TRUE;
1238  test_agents(scheduler);
1239 }
1240 
1241 /* ************************************************************************** */
1242 /* **** Version refresh ***************************************************** */
1243 /* ************************************************************************** */
1244 
1248 typedef struct
1249 {
1250  scheduler_t* scheduler;
1251  int count;
1253 
1270 static gboolean version_refresh_kill_agent(int* pid_ptr, agent_t* agent,
1271  version_refresh_ctx* ctx)
1272 {
1273  if (agent == NULL)
1274  {
1275  return FALSE;
1276  }
1277 
1279 
1280  /* leave agents that have already started; only respawn the not-yet-working ones */
1281  if (agent->status != AG_SPAWNED)
1282  {
1283  return FALSE;
1284  }
1285 
1286  /* kill() directly, not agent_kill(): return_code=0 makes the death event
1287  * re-queue the still-CHECKEDOUT job. Kill the process group too. */
1288  agent->return_code = 0;
1289  log_printf("NOTE: version refresh: sending SIGKILL to agent pid %d type \"%s\"\n",
1290  agent->pid, agent->type ? agent->type->name : "?");
1291  kill(-agent->pid, SIGKILL);
1292  ctx->count++;
1293  return FALSE;
1294 }
1295 
1306 void scheduler_version_refresh(scheduler_t* scheduler, void* unused)
1307 {
1308  version_refresh_ctx ctx;
1309  ctx.scheduler = scheduler;
1310  ctx.count = 0;
1311 
1312  log_printf("NOTE: scheduler_version_refresh: killing all running agents "
1313  "for version update\n");
1314 
1315  g_tree_foreach(scheduler->agents,
1316  (GTraverseFunc)version_refresh_kill_agent, &ctx);
1317 
1318  log_printf("NOTE: scheduler_version_refresh: sent SIGKILL to %d agent(s); "
1319  "they will respawn automatically\n", ctx.count);
1320 }
1321 
1328 gint string_is_num(gchar* str)
1329 {
1330  int len = strlen(str);
1331  int i;
1332 
1333  for(i = 0; i < len; i++)
1334  if(!isdigit(str[i]))
1335  return FALSE;
1336  return TRUE;
1337 }
1338 
1347 gint string_compare(gconstpointer a, gconstpointer b, gpointer user_data)
1348 {
1349  return strcmp((char*)a, (char*)b);
1350 }
1351 
1361 gint int_compare(gconstpointer a, gconstpointer b, gpointer user_data)
1362 {
1363  return *(int*)a - *(int*)b;
1364 }
agent_t * agent_init(scheduler_t *scheduler, host_t *host, job_t *job)
Allocate and spawn a new agent.
Definition: agent.c:952
void agent_meta_version_reset(meta_agent_t *ma)
Reset a meta_agent's cached version fields under version_lock.
Definition: agent.c:290
void agent_update_event(scheduler_t *scheduler, void *unused)
Definition: agent.c:1390
void agent_destroy(agent_t *agent)
Frees the memory associated with an agent.
Definition: agent.c:1088
int add_meta_agent(GTree *meta_agents, char *name, char *cmd, int max, int spc)
Definition: agent.c:1695
void test_agents(scheduler_t *scheduler)
Calls the agent test function for every type of agent.
Definition: agent.c:1669
void kill_agents(scheduler_t *scheduler)
Call the agent_kill function for every agent within the system.
Definition: agent.c:1680
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:1720
void agent_death_event(scheduler_t *scheduler, pid_t *pid)
Definition: agent.c:1120
void meta_agent_destroy(meta_agent_t *ma)
Definition: agent.c:934
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:437
job_t * next_job(GSequence *job_queue)
Gets the next job from the job queue.
Definition: job.c:744
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:764
void job_remove_agent(job_t *job, GTree *job_list, void *agent)
Definition: job.c:494
gint job_compare(gconstpointer a, gconstpointer b, gpointer user_data)
Used to compare two different jobs in the priority queue.
Definition: job.c:135
void job_destroy(job_t *job)
Definition: job.c:202
uint32_t active_jobs(GTree *job_list)
Gets the number of jobs that are not paused.
Definition: job.c:783
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:981
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:771
void database_update_event(scheduler_t *scheduler, void *unused)
Checks the job queue for any new entries.
Definition: database.c:846
void email_init(scheduler_t *scheduler)
Loads information about the email that will be sent for job notifications.
Definition: database.c:558
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:1197
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:921
int scheduler_daemonize(scheduler_t *scheduler)
Daemonizes the scheduler.
Definition: scheduler.c:1179
GThread * main_thread
Pointer to the main thread.
Definition: scheduler.c:60
void scheduler_version_refresh(scheduler_t *scheduler, void *unused)
Event run when the scheduler's own version changed.
Definition: scheduler.c:1306
gint string_is_num(gchar *str)
Checks if a string is entirely composed of numeric characters.
Definition: scheduler.c:1328
void g_tree_clear(GTree *tree)
Clears the contents of a GTree.
Definition: scheduler.c:899
static gboolean isMaxLimitReached(meta_agent_t *agent)
Check if the current agent's max limit is respected.
Definition: scheduler.c:409
void scheduler_close_event(scheduler_t *scheduler, void *killed)
Sets the closing flag and possibly kills all currently running agents.
Definition: scheduler.c:1220
void scheduler_test_agents(scheduler_t *scheduler, void *unused)
Event used when the scheduler tests the agents.
Definition: scheduler.c:1235
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:885
void scheduler_update(scheduler_t *scheduler)
Update function called after every event.
Definition: scheduler.c:564
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:846
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:1361
void scheduler_foss_config(scheduler_t *scheduler)
Loads the configuration data from fossology.conf.
Definition: scheduler.c:1031
gint string_compare(gconstpointer a, gconstpointer b, gpointer user_data)
Definition: scheduler.c:1347
static void reap_stale_jobs(scheduler_t *scheduler)
Reap CHECKEDOUT jobs that never had an agent spawned.
Definition: scheduler.c:482
void set_usr_grp(gchar *process_name, fo_conf *config)
Definition: scheduler.c:732
static gboolean collect_stale_jobs(gpointer key, gpointer val, gpointer data)
GTraverseFunc: collect CHECKEDOUT jobs with no agents that have been waiting longer than CONF_agent_u...
Definition: scheduler.c:444
static gboolean version_refresh_kill_agent(int *pid_ptr, agent_t *agent, version_refresh_ctx *ctx)
GTraverseFunc: respawn a not-yet-working agent on the new binary.
Definition: scheduler.c:1270
#define TEST_ERROR(error,...)
Definition: scheduler.c:44
int kill_scheduler(int force)
Kills all other running scheduler.
Definition: scheduler.c:795
void scheduler_destroy(scheduler_t *scheduler)
Free any memory associated with a scheduler_t.
Definition: scheduler.c:364
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:256
#define CONF_VARIABLES_TYPES(apply)
Definition: scheduler.h:248
#define AGENT_CONF
Agent conf location.
Definition: scheduler.h:123
Definition: agent.h:100
agent_status status
the state of execution the agent is currently in
Definition: agent.h:106
meta_agent_t * type
the type of agent this is i.e. bucket, copyright...
Definition: agent.h:102
uint8_t return_code
what was returned by the agent when it disconnected
Definition: agent.h:126
pid_t pid
the pid of the process this agent is running in
Definition: agent.h:110
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:52
int32_t id
The identifier for this job.
Definition: job.h:74
job_status status
The current status for the job.
Definition: job.h:62
char * required_host
If not NULL, this job must run on a specific host machine.
Definition: job.h:55
GList * finished_agents
The list of agents that have completed their tasks.
Definition: job.h:57
gchar * message
Message that will be sent with job notification email.
Definition: job.h:70
time_t checkedout_at
Timestamp when job entered JB_CHECKEDOUT (for stale detection grace period)
Definition: job.h:77
GList * failed_agents
The list of agents that failed while working.
Definition: job.h:58
char * agent_type
The type of agent used to analyze the data.
Definition: job.h:54
gchar * data
The data associated with this job (jq_args)
Definition: job.h:63
GList * running_agents
The list of agents assigned to this job that are still working.
Definition: job.h:56
Definition: logging.h:35
int max_run
the maximum number that can run at once -1 if no limit
Definition: agent.h:85
char name[256]
the name associated with this agent i.e. nomos, copyright...
Definition: agent.h:83
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:191
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:189
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:190
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
gchar * scheduler_version
The version string read from VERSION file at last (re)load.
Definition: scheduler.h:186
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
Context for collect_stale_jobs traversal: carries current time so each node can apply a per-job grace...
Definition: scheduler.c:427
GList * list
Accumulates stale job_t pointers.
Definition: scheduler.c:428
time_t now
Current time snapshot for the traversal.
Definition: scheduler.c:429
Context structure for the version-refresh tree traversal.
Definition: scheduler.c:1249