Implement the with pid execution.
This expects the process to be spawned in the background.
After some review, I decided to remove "use" and "create", replacing those with "pid_file".
The reasons are:
- For "use", the spawned service manages the pid file, so it would be overly complicated to try and manage it in addition to the spawned service.
- For "create", if the process is to go into the background, in order to manage it then there would still need to be a running process (this defeats the purpose).
When the termination signal is received, then inform any background process spawned by the controller program to exit, based on the existence of the pid file.
controller_cache_delete_simple(&process->cache);
controller_rule_delete_simple(&process->rule);
+ f_string_dynamic_resize(0, &process->path_pid);
+
f_macro_array_lengths_t_delete_simple(process->stack)
}
#endif // _di_controller_process_delete_simple_
#define controller_string_parameter "parameter"
#define controller_string_path "path"
#define controller_string_pause "pause"
- #define controller_string_pid "pid"
+ #define controller_string_pid_file "pid_file"
#define controller_string_processor "processor"
#define controller_string_program "program"
#define controller_string_ready "ready"
#define controller_string_parameter_length 9
#define controller_string_path_length 4
#define controller_string_pause_length 5
- #define controller_string_pid_length 3
+ #define controller_string_pid_file_length 8
#define controller_string_processor_length 9
#define controller_string_program_length 7
#define controller_string_ready_length 5
const static f_string_t controller_string_parameter_s = controller_string_parameter;
const static f_string_t controller_string_path_s = controller_string_path;
const static f_string_t controller_string_pause_s = controller_string_pause;
- const static f_string_t controller_string_pid_s = controller_string_pid;
+ const static f_string_t controller_string_pid_file_s = controller_string_pid_file;
const static f_string_t controller_string_processor_s = controller_string_processor;
const static f_string_t controller_string_program_s = controller_string_program;
const static f_string_t controller_string_ready_s = controller_string_ready;
};
enum {
- controller_rule_action_type_create = 1,
- controller_rule_action_type_freeze,
+ controller_rule_action_type_freeze = 1,
controller_rule_action_type_group,
controller_rule_action_type_kill,
controller_rule_action_type_pause,
+ controller_rule_action_type_pid_file,
controller_rule_action_type_reload,
controller_rule_action_type_restart,
controller_rule_action_type_resume,
controller_rule_action_type_start,
controller_rule_action_type_stop,
controller_rule_action_type_thaw,
- controller_rule_action_type_use,
controller_rule_action_type_user,
controller_rule_action_type_with,
};
#endif // _di_controller_rules_t_
/**
+ * A set of codes representing different with flags.
+ */
+#ifndef _di_controller_with_defines_
+ #define controller_with_full_path 0x1
+#endif // _di_controller_with_defines_
+
+/**
* A Rule Process.
*
* This refers to "process" as in the processing of a single rule for the given Rule ID and does not refer to "process" as in a CPU Process.
controller_cache_t cache;
f_array_lengths_t stack;
+ f_string_dynamic_t path_pid;
+
controller_rule_t rule;
void *main_data;
f_thread_condition_t_initialize, \
controller_cache_t_initialize, \
f_array_lengths_t_initialize, \
+ f_string_dynamic_t_initialize, \
controller_rule_t_initialize, \
0, \
0, \
}
#endif // _di_controller_file_pid_delete_
+#ifndef _di_controller_file_pid_read_
+ f_status_t controller_file_pid_read(const f_string_static_t path, pid_t *pid) {
+
+ *pid = 0;
+
+ f_status_t status = f_file_exists(path.string);
+ if (F_status_is_error(status)) return status;
+
+ if (status != F_true) {
+ return F_data_not;
+ }
+
+ f_file_t pid_file = f_file_t_initialize;
+
+ status = f_file_stream_open(path.string, f_file_open_mode_read_s, &pid_file);
+ if (F_status_is_error(status)) return status;
+
+ f_string_dynamic_t pid_buffer = f_string_dynamic_t_initialize;
+
+ status = f_file_stream_read(pid_file, 1, &pid_buffer);
+
+ if (F_status_is_error_not(status)) {
+ status = f_file_stream_close(F_true, &pid_file);
+ }
+
+ if (F_status_is_error_not(status)) {
+ f_number_unsigned_t number = 0;
+ f_string_range_t range = f_macro_string_range_t_initialize(pid_buffer.used);
+
+ for (; range.start < pid_buffer.used; ++range.start) {
+ if (!isspace(pid_buffer.string[range.start])) break;
+ } // for
+
+ for (; range.stop > 0; --range.stop) {
+ if (!isspace(pid_buffer.string[range.stop])) break;
+ } // for
+
+ status = fl_conversion_string_to_decimal_unsigned(pid_buffer.string, range, &number);
+
+ if (F_status_is_error_not(status)) {
+ *pid = (pid_t) number;
+ }
+ }
+
+ f_macro_string_dynamic_t_delete_simple(pid_buffer);
+
+ return status;
+ }
+#endif // _di_controller_file_pid_read_
+
#ifndef _di_controller_find_process_
f_status_t controller_find_process(const f_string_static_t alias, const controller_processs_t processs, f_array_length_t *at) {
* Errors (with error bit) from: f_file_stream_close().
* Errors (with error bit) from: f_file_stream_open().
* Errors (with error bit) from: f_file_stream_read().
+ * Errors (with error bit) from: fl_conversion_string_to_decimal_unsigned()
*/
#ifndef _di_controller_file_pid_delete_
f_status_t controller_file_pid_delete(const pid_t pid, const f_string_static_t path) f_gcc_attribute_visibility_internal;
#endif // _di_controller_file_pid_delete_
/**
+ * Read the PID from a PID file.
+ *
+ * @param path
+ * The file path to the pid file to create.
+ * @param pid
+ * The PID to be read.
+ *
+ * @return
+ * F_none on success.
+ *
+ * Errors (with error bit) from: f_file_stream_close().
+ * Errors (with error bit) from: f_file_stream_open().
+ * Errors (with error bit) from: f_file_stream_read().
+ * Errors (with error bit) from: fl_conversion_string_to_decimal_unsigned()
+ */
+#ifndef _di_controller_file_pid_read_
+ f_status_t controller_file_pid_read(const f_string_static_t path, pid_t *pid) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_file_pid_read_
+
+/**
* Find an existing process.
*
* Do not confuse this with a process in the context of a PID.
f_string_static_t buffer = f_string_static_t_initialize;
switch (type) {
- case controller_rule_action_type_create:
- buffer.string = controller_string_create_s;
- buffer.used = controller_string_create_length;
- break;
-
case controller_rule_action_type_group:
buffer.string = controller_string_group_s;
buffer.used = controller_string_group_length;
buffer.used = controller_string_kill_length;
break;
+ case controller_rule_action_type_pid_file:
+ buffer.string = controller_string_pid_file_s;
+ buffer.used = controller_string_pid_file_length;
+ break;
+
case controller_rule_action_type_restart:
buffer.string = controller_string_restart_s;
buffer.used = controller_string_restart_length;
buffer.used = controller_string_stop_length;
break;
- case controller_rule_action_type_use:
- buffer.string = controller_string_use_s;
- buffer.used = controller_string_use_length;
- break;
-
case controller_rule_action_type_user:
buffer.string = controller_string_user_s;
buffer.used = controller_string_user_length;
f_array_length_t k = 0;
f_string_dynamic_t *pid_file = 0;
- uint8_t pid_type = 0;
- bool with_full_path = F_false;
+ uint8_t with = 0;
// child processes should receive all signals and handle the signals as they see fit.
f_signal_how_t signals = f_signal_how_t_initialize;
if (process->rule.items.array[i].type == controller_rule_item_type_setting) continue;
- with_full_path = F_false;
+ with = 0;
for (j = 0; j < process->rule.items.array[i].actions.used; ++j) {
for (k = 0; k < process->rule.items.array[i].actions.array[j].parameters.used; ++k) {
if (fl_string_dynamic_compare_string(controller_string_full_path_s, process->rule.items.array[i].actions.array[j].parameters.array[k], controller_string_full_path_length) == F_equal_to) {
- with_full_path = F_true;
-
- break;
+ with |= controller_with_full_path;
}
} // for
-
- if (with_full_path) break;
}
} // for
execute_set.parameter.data = 0;
execute_set.parameter.option = fl_execute_parameter_option_threadsafe | fl_execute_parameter_option_return;
- if (with_full_path) {
+ if (with & controller_with_full_path) {
execute_set.parameter.option |= fl_execute_parameter_option_path;
}
}
else if (process->rule.items.array[i].type == controller_rule_item_type_service) {
pid_file = 0;
- pid_type = 0;
for (k = 0; k < process->rule.items.array[i].actions.used; ++k) {
- if (process->rule.items.array[i].actions.array[k].type != controller_rule_action_type_create && process->rule.items.array[i].actions.array[k].type != controller_rule_action_type_use) {
+ if (process->rule.items.array[i].actions.array[k].type != controller_rule_action_type_pid_file) {
continue;
}
}
pid_file = &process->rule.items.array[i].actions.array[k].parameters.array[0];
- pid_type = process->rule.items.array[i].actions.array[k].type;
} // for
if (pid_file) {
- status = controller_rule_execute_pid_with(pid_file, pid_type, process->rule.items.array[i].type, process->rule.items.array[i].actions.array[j], 0, process->rule.items.array[i].actions.array[j].parameters, options, main, &execute_set, process);
+ status = controller_rule_execute_pid_with(*pid_file, process->rule.items.array[i].type, process->rule.items.array[i].actions.array[j], 0, process->rule.items.array[i].actions.array[j].parameters, options, with, main, &execute_set, process);
if (status == F_child || status == F_signal || F_status_set_fine(status) == F_lock) break;
}
else if (process->rule.items.array[i].type == controller_rule_item_type_utility) {
pid_file = 0;
- pid_type = 0;
for (k = 0; k < process->rule.items.array[i].actions.used; ++k) {
- if (process->rule.items.array[i].actions.array[k].type != controller_rule_action_type_create && process->rule.items.array[i].actions.array[k].type != controller_rule_action_type_use) {
+ if (process->rule.items.array[i].actions.array[k].type != controller_rule_action_type_pid_file) {
continue;
}
}
pid_file = &process->rule.items.array[i].actions.array[k].parameters.array[0];
- pid_type = process->rule.items.array[i].actions.array[k].type;
} // for
if (pid_file) {
execute_set.parameter.data = &process->rule.items.array[i].actions.array[j].parameters.array[0];
- status = controller_rule_execute_pid_with(pid_file, pid_type, process->rule.items.array[i].type, process->rule.items.array[i].actions.array[j], process->rule.script.used ? process->rule.script.string : controller_default_program_script, arguments_none, options, main, &execute_set, process);
+ status = controller_rule_execute_pid_with(*pid_file, process->rule.items.array[i].type, process->rule.items.array[i].actions.array[j], process->rule.script.used ? process->rule.script.string : controller_default_program_script, arguments_none, options, with, main, &execute_set, process);
if (status == F_child || status == F_signal || F_status_set_fine(status) == F_lock) break;
if (!WIFEXITED(result)) {
status = F_status_set_error(F_failure);
}
+ else {
+ status = F_none;
+ }
}
else {
if (!main.thread->enabled) {
#endif // _di_controller_rule_execute_foreground_
#ifndef _di_controller_rule_execute_pid_with_
- f_status_t controller_rule_execute_pid_with(const f_string_dynamic_t *pid_file, const uint8_t pid_type, const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, const controller_main_t main, controller_execute_set_t * const execute_set, controller_process_t *process) {
+ f_status_t controller_rule_execute_pid_with(const f_string_dynamic_t pid_file, const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, const uint8_t with, const controller_main_t main, controller_execute_set_t * const execute_set, controller_process_t *process) {
f_status_t status = F_none;
f_status_t status_lock = F_none;
int result = 0;
pid_t id_child = 0;
- // @todo check to see if pid file exists.
+ process->path_pid.used = 0;
+
+ status = f_file_exists(pid_file.string);
+
+ if (F_status_is_error(status)) {
+ fll_error_file_print(main.data->error, F_status_set_fine(status), "f_file_exists", F_true, pid_file.string, "find", fll_error_file_type_file);
+
+ return status;
+ }
+
+ if (status == F_true) {
+ fll_error_file_print(main.data->error, F_file_found, "f_file_exists", F_true, pid_file.string, "create PID", fll_error_file_type_file);
+
+ return F_status_set_error(F_file_found);
+ }
+
+ status = controller_string_dynamic_append_terminated(pid_file, &process->path_pid);
+
+ if (F_status_is_error(status)) {
+ fll_error_print(main.data->error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
+
+ return status;
+ }
if (options & controller_process_option_simulate) {
controller_lock_error_critical_print(main.data->error, F_status_set_fine(status_lock), F_true, main.thread);
}
- // have the parent wait for the child process to finish. @todo do not wait, this is a background execution! instead, wait for pid file or timeout (or perhaps optional create the pid file).
+ // the child process should perform the change into background, therefore it is safe to wait for the child to exit (another process is spawned).
waitpid(id_child, &result, 0);
if (!main.thread->enabled) {
return F_status_set_error(F_lock);
}
- // remove the pid now that waidpid() has returned. @todo do not clear until forked execution is known to have exited, this is a background execution
+ // remove the pid now that waidpid() has returned.
process->child = 0;
f_thread_unlock(&process->lock);
}
// this must explicitly check for 0 (as opposed to checking (!result)).
+ // @todo expand this to provide user more control over what is or is not an error to designate as a failure.
if (!WIFEXITED(result)) {
status = F_status_set_error(F_failure);
}
-
- // @fixme needs a custom option to desginate what is an error.
+ else {
+ status = F_none;
+ }
}
else {
if (!main.thread->enabled) {
break;
}
- if (fl_string_dynamic_compare_string(controller_string_create_s, cache->action.name_action, controller_string_create_length) == F_equal_to) {
- type = controller_rule_action_type_create;
- }
- else if (fl_string_dynamic_compare_string(controller_string_group_s, cache->action.name_action, controller_string_group_length) == F_equal_to) {
+ if (fl_string_dynamic_compare_string(controller_string_group_s, cache->action.name_action, controller_string_group_length) == F_equal_to) {
type = controller_rule_action_type_group;
}
else if (fl_string_dynamic_compare_string(controller_string_kill_s, cache->action.name_action, controller_string_kill_length) == F_equal_to) {
else if (fl_string_dynamic_compare_string(controller_string_pause_s, cache->action.name_action, controller_string_pause_length) == F_equal_to) {
type = controller_rule_action_type_pause;
}
+ else if (fl_string_dynamic_compare_string(controller_string_pid_file_s, cache->action.name_action, controller_string_pid_file_length) == F_equal_to) {
+ type = controller_rule_action_type_pid_file;
+ }
else if (fl_string_dynamic_compare_string(controller_string_restart_s, cache->action.name_action, controller_string_restart_length) == F_equal_to) {
type = controller_rule_action_type_restart;
}
else if (fl_string_dynamic_compare_string(controller_string_stop_s, cache->action.name_action, controller_string_stop_length) == F_equal_to) {
type = controller_rule_action_type_stop;
}
- else if (fl_string_dynamic_compare_string(controller_string_use_s, cache->action.name_action, controller_string_use_length) == F_equal_to) {
- type = controller_rule_action_type_use;
- }
else if (fl_string_dynamic_compare_string(controller_string_user_s, cache->action.name_action, controller_string_user_length) == F_equal_to) {
type = controller_rule_action_type_user;
}
}
if (multiple) {
- if (type == controller_rule_action_type_create || type == controller_rule_action_type_group || type == controller_rule_action_type_use || type == controller_rule_action_type_user) {
+ if (type == controller_rule_action_type_group || type == controller_rule_action_type_pid_file || type == controller_rule_action_type_user) {
if (main.data->error.verbosity != f_console_verbosity_quiet) {
f_thread_mutex_lock(&main.thread->lock.print);
*
* @param pid_file
* The path to the PID file.
- * @param pid_type
- * The type of the PID file, either "controller_rule_action_type_create" or "controller_rule_action_type_use".
* @param type
* The item type code.
* @param action
* @param options
* A number using bits to represent specific boolean options.
* If bit controller_process_option_simulate, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
+ * @param with
+ * The "with" option flags.
* @param main
* The main data.
* @param execute_set
*
* @return
* F_none on success.
- * F_busy on successful execute in asynchronous mode (executed process may or may not fail later on).
* F_child on child process exiting.
* F_signal on (exit) signal received.
*
* F_lock (with error bit) if failed to re-establish read lock on process->lock while returning.
+ * F_file_found (with error bit) if the PID file already exists.
*
* Errors (with error bit) from: fll_execute_program().
*
* @see fll_execute_program()
*/
#ifndef _di_controller_rule_execute_pid_with_
- extern f_status_t controller_rule_execute_pid_with(const f_string_dynamic_t *pid_file, const uint8_t pid_type, const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, const controller_main_t main, controller_execute_set_t * const execute_set, controller_process_t *process) f_gcc_attribute_visibility_internal;
+ extern f_status_t controller_rule_execute_pid_with(const f_string_dynamic_t pid_file, const uint8_t type, const controller_rule_action_t action, const f_string_t program, const f_string_dynamics_t arguments, const uint8_t options, const uint8_t with, const controller_main_t main, controller_execute_set_t * const execute_set, controller_process_t *process) f_gcc_attribute_visibility_internal;
#endif // _di_controller_rule_execute_pid_with_
/**
continue;
}
+ // if process has a pid file, then it is running in the background, only cleanup if the pid file no longer exists.
+ if (process->path_pid.used && f_file_exists(process->path_pid.string) == F_true) {
+ f_thread_unlock(&process->active);
+ f_thread_unlock(&process->lock);
+
+ continue;
+ }
+
f_thread_unlock(&process->lock);
// close any still open thread.
controller_cache_delete_simple(&process->cache);
f_type_array_lengths_resize(0, &process->stack);
+ // deallocate the pid file.
+ if (process->path_pid.used) {
+ process->path_pid.used = 0;
+ f_string_dynamic_resize(0, &process->path_pid);
+ }
+
// deallocate any rules in the space that is declared to be unused.
if (i >= main->thread->processs.used) {
controller_rule_delete_simple(&process->rule);
controller_process_t *process = 0;
f_array_length_t i = 0;
+ pid_t pid = 0;
if (main->thread->id_cleanup) {
f_thread_cancel(main->thread->id_cleanup);
if (process->child > 0) {
f_signal_send(F_signal_termination, process->child);
}
+
+ if (process->path_pid.used && f_file_exists(process->path_pid.string) == F_true) {
+ status = controller_file_pid_read(process->path_pid, &pid);
+
+ if (pid) {
+ f_signal_send(F_signal_termination, pid);
+ }
+ }
} // for
for (i = 0; i < main->thread->processs.used; ++i) {
process = main->thread->processs.array[i];
do {
+ if (!process->id_thread) break;
+
controller_time(0, controller_thread_exit_process_cancel_wait, &time);
status = f_thread_join_timed(process->id_thread, time, 0);
process->id_thread = 0;
}
- spent++;
+ ++spent;
} while (status == F_time && spent < controller_thread_exit_process_cancel_total);
+
+ if (process->path_pid.used) {
+ for (; spent < controller_thread_exit_process_cancel_total; ++spent) {
+
+ if (f_file_exists(process->path_pid.string) == F_true) {
+ status = controller_file_pid_read(process->path_pid, &pid);
+
+ if (pid) {
+
+ // a hackish way to determine if the pid exists while waiting.
+ if (getpgid(pid) >= 0) {
+ time.tv_sec = 0;
+ time.tv_nsec = controller_thread_exit_process_cancel_wait;
+
+ nanosleep(&time, 0);
+ }
+ else {
+ f_file_remove(process->path_pid.string);
+ process->path_pid.used = 0;
+
+ break;
+ }
+ }
+ else {
+ break;
+ }
+ }
+ else {
+ break;
+ }
+ } // for
+ }
} // for
for (i = 0; i < main->thread->processs.size; ++i) {
process->child = 0;
process->id_thread = 0;
}
+
+ if (process->path_pid.used) {
+ if (f_file_exists(process->path_pid.string) == F_true) {
+ status = controller_file_pid_read(process->path_pid, &pid);
+
+ if (pid) {
+ f_signal_send(F_signal_kill, pid);
+ }
+
+ f_file_remove(process->path_pid.string);
+ process->path_pid.used = 0;
+ }
+ }
} // for
}
#endif // _di_controller_thread_process_cancel_
nice 15
service:
- use /var/run/sshd.pid
+ pid_file /var/run/sshd.pid
with full_path
start sshd
# fss-000d
-# sleeper rule whose program creates its own PID file, sleep for a while, removes PID file, and returns.
+# sleeper rule whose program creates its own PID file, runs in the background, sleep for a while, removes PID file, and returns.
setting:
name "Sleeper #1"
nice 10
utility:
- use /tmp/sleeper_1.pid
+ pid_file /tmp/sleeper_1.pid
start {
\#!/bin/bash
return 1
fi
- echo "$$" > /tmp/sleeper_1.pid
+ echo "$BASHPID" > /tmp/sleeper_1.pid
echo "Sleeper 1, now sleeping."
sleep 20m
return 0
\}
- main
+ main &
}
# fss-000d
-# sleeper rule whose program does not create its own PID file, sleeps for a while and returns.
+# sleeper rule whose program creates its own PID file, runs in the background, sleep for a while, removes PID file, and returns.
setting:
name "Sleeper #2"
nice 10
utility:
- create /tmp/sleeper_2.pid
+ pid_file /tmp/sleeper_2.pid
start {
\#!/bin/bash
main() {
+ if [[ -f /tmp/sleeper_2.pid ]] ; then
+ echo "Failure: pid file '/tmp/sleeper_1.pid' already exists."
+ return 1
+ fi
+
+ echo "$BASHPID" > /tmp/sleeper_2.pid
+
echo "Sleeper 2, now sleeping."
sleep 25m
echo "Sleeper 2, done sleeping."
+ rm -f /tmp/sleeper_2.pid
return 0
\}
- main
+ main &
}
capability "all="
service:
- use /var/run/tty/tty1.pid
+ pid_file /var/run/tty/tty1.pid
start qingy tty1 -d -l -n -t
service:
- use /var/run/tty/tty2.pid
+ pid_file /var/run/tty/tty2.pid
start qingy tty2 -d -l -n -t
service:
- use /var/run/tty/tty3.pid
+ pid_file /var/run/tty/tty3.pid
start qingy tty3 -d -l -n -t
service:
- use /var/run/tty/tty4.pid
+ pid_file /var/run/tty/tty4.pid
start qingy tty4 -d -l -n -t
nice 15
service:
- use /var/run/dbus/dbus.pid
+ pid_file /var/run/dbus/dbus.pid
start dbus-daemon --system --fork
service:
# @todo consider adding support for IKI to make "/var/run/logger/logger.pid" a variable.
- use /var/run/logger/logger.pid
+ pid_file /var/run/logger/logger.pid
start metalog -B -p /var/run/logger/logger.pid -C /etc/logger.conf
}
service:
- use /var/run/mouse/mouse.pid
+ pid_file /var/run/mouse/mouse.pid
# @todo
start gpm -m [device] -t [protocol] [options]
When "reload", "start", or "stop" Content are not provided, then no respective action is performed.
Commands are conditionally available depending on the presence of these, such as if "stop" is not provided then "stop" (and "restart") will not be available for the "control" program(s) to use.
- The "create" Content designates that this controller program to create the PID file after successfully starting a Service or Utility.
- The "use" Content designates that the called program will provide the PID file after successfully starting the Service or Utility.
- For both "create" and "program" the PID file is expected to only exist on success and the existence thereof designates the success or failure.
+ The "pid_file" Content designates the path to the PID file created by the called program.
The "with" Content designates special flags designating very specific behavior to be applied to any single Rule Type.
The following flags are supported:
"with": One or more Content representing special options for the Rule Type.
The "service" and "utility" Rule Types allow the following the FSS-0001 (Extended)\:
- "create": One Content representing the path to a PID file.
- "use": One Content representing the path to a PID file.
+ "pid_file": One Content representing the path to a PID file.
"with": One or more Content representing special options for the Rule Type.
The "command" and "service" Rule Types allow the following the FSS-0003 (Extended List)\: