From 3cbff2b6ae290b6e60f800d8211766b25cb26a4a Mon Sep 17 00:00:00 2001 From: Matthew Zito Date: Sat, 11 Nov 2023 23:36:32 -0800 Subject: [PATCH] docs: add doc comments for posterity pt 1 --- scripts/proc-count.sh | 2 + src/cli.c | 6 ++ src/cli.h | 6 ++ src/constants.h | 1 + src/cronentry.c | 2 +- src/cronentry.h | 38 +++++++++++++ src/crontab.c | 54 +++++++++++------- src/crontab.h | 73 +++++++++++++++++++++++- src/daemon.h | 5 ++ src/job.c | 125 +++++++++++++++++++++++++++++++----------- src/job.h | 75 +++++++++++++++++++++++-- src/log.c | 11 +--- src/log.h | 14 ++++- src/main.c | 28 ++-------- src/opt-constants.h | 3 + src/util.c | 20 +++++++ src/util.h | 15 +++++ t/unit/crontab_test.c | 6 +- t/unit/main.c | 36 ++++-------- 19 files changed, 397 insertions(+), 123 deletions(-) create mode 100755 scripts/proc-count.sh diff --git a/scripts/proc-count.sh b/scripts/proc-count.sh new file mode 100755 index 0000000..5192995 --- /dev/null +++ b/scripts/proc-count.sh @@ -0,0 +1,2 @@ +#!/bin/sh +ps aux | grep 'defunct' | grep -v 'grep' | awk '{print $2}' | wc -l diff --git a/src/cli.c b/src/cli.c index c2365e3..e1c7c83 100644 --- a/src/cli.c +++ b/src/cli.c @@ -3,6 +3,12 @@ #include "commander/commander.h" #include "constants.h" +/** + * Sets the log file CLI option. + * + * Optionally specify a path to which logs will be written. + * Otherwise, syslog or a comparable facility will be utilized. + */ static void setopt_logfile(command_t* self) { opts.log_file = (char*)self->arg; } diff --git a/src/cli.h b/src/cli.h index c73d8ae..6ed6911 100644 --- a/src/cli.h +++ b/src/cli.h @@ -3,6 +3,9 @@ #include +/** + * CLI configuration options. + */ typedef struct { bool use_syslog; char* log_file; @@ -10,6 +13,9 @@ typedef struct { extern CliOptions opts; +/** + * Initializes the CLI configuration. + */ void cli_init(int argc, char** argv); #endif /* CLI_H */ diff --git a/src/constants.h b/src/constants.h index eaf6131..ef2a327 100644 --- a/src/constants.h +++ b/src/constants.h @@ -31,6 +31,7 @@ extern pid_t daemon_pid; extern char hostname[SMALL_BUFFER]; extern array_t *job_queue; +extern array_t *mail_queue; extern const char *job_state_names[]; diff --git a/src/cronentry.c b/src/cronentry.c index a5b1f88..8a3f9ac 100644 --- a/src/cronentry.c +++ b/src/cronentry.c @@ -11,7 +11,7 @@ CronEntry* new_cron_entry(char* raw, time_t curr, Crontab* ct) { // TODO: Lift out if (parse_entry(entry, raw) != OK) { - printlogf("Failed to parse entry line %s\n", raw); + printlogf("[ERROR] Failed to parse entry line %s\n", raw); return NULL; } diff --git a/src/cronentry.h b/src/cronentry.h index a618fae..ba9b388 100644 --- a/src/cronentry.h +++ b/src/cronentry.h @@ -6,17 +6,55 @@ #include "ccronexpr/ccronexpr.h" #include "crontab.h" +/** + * Represents a single line (entry) in a crontab. + */ typedef struct { + /** + * The pre-parsed cron expression. + */ cron_expr *expr; + /** + * The original cron schedule specification in the entry. + */ char *schedule; + /** + * The command specified by the entry. + */ char *cmd; + /** + * The next execution time, relative to the current crond iteration. + */ time_t next; + /** + * A unique identifier for the entry. Used for logging and non-critical + * functions. + */ unsigned int id; + /** + * A pointer to the entry's parent crontab. + */ Crontab *parent; } CronEntry; +/** + * Parses the raw line from the crontab file as an entry for the provided + * crontab object. + * + * @param raw The raw line/entry. + * @param curr The current crond iteration time. + * @param ct The crontab object to which this entry belongs. We pass in the + * crontab because we want the entry to have a pointer to its parent. + */ CronEntry *new_cron_entry(char *raw, time_t curr, Crontab *ct); +/** + * Renews the `next` field on the given cron entry based on the current crond + * iteration time. + * + * @param entry The cron entry to renew. + * @param curr The current crond iteration time. + */ void renew_cron_entry(CronEntry *entry, time_t curr); #endif /* CRONENTRY_H */ diff --git a/src/crontab.c b/src/crontab.c index 4677ca2..9383d71 100644 --- a/src/crontab.c +++ b/src/crontab.c @@ -16,13 +16,7 @@ #include "parser.h" #include "util.h" -/* - * Because crontab/at files may be owned by their respective users we - * take extreme care in opening them. If the OS lacks the O_NOFOLLOW - * we will just have to live without it. In order for this to be an - * issue an attacker would have to subvert group CRON_GROUP. - * TODO: comment - */ +// Some systems won't have this option and we'll just have to live without it. #ifndef O_NOFOLLOW #define O_NOFOLLOW 0 #endif @@ -30,6 +24,16 @@ #define MAXENTRIES 256 #define RW_BUFFER 1024 +/** + * Returns true if the file represented by the stat entity is not owned by the + * user (represented by the pw entity). + * + * @param statbuf The stat struct for the file we're evaluating. + * @param pw The user's passwd entry. + * @param uname The username. + * @return true The file is NOT owned by the given user. + * @return false The file IS owned by the given user - yay! + */ static bool file_not_owned_by(struct stat* statbuf, struct passwd* pw, char* uname) { return statbuf->st_uid != ROOT_UID && @@ -37,9 +41,17 @@ static bool file_not_owned_by(struct stat* statbuf, struct passwd* pw, strcmp(uname, pw->pw_name) != 0); } +/** + * Modifies and finalizes the given crontab's environment data by adding any + * missing env vars. For example, we check for the user's home directory, shell, + * etc and set those. + * + * @param ct The crontab whose environment we're finalizing. + */ static void complete_env(Crontab* ct) { hash_table* vars = ct->vars; +// We don't want to have to create users for unit tests. #ifndef UNIT_TEST struct passwd* pw = getpwnam(ct->uname); if (pw) { @@ -60,16 +72,19 @@ static void complete_env(Crontab* ct) { } } else { printlogf( - "Something went really awry and uname=%s wasn't found in the system " - "user db\n", + "[ERROR] Something went really awry and uname=%s wasn't found in the " + "system user db\n", ct->uname); } #endif // TODO: We could totally hoist this logic - the var matching function // actually returns the full key=value string in the first element of the - // matches array I was just lazy and really wanted to get the crond up and - // running + // matches array. I was just lazy and really wanted to get the crond up and + // running. + + // Fill out the envp using the vars map. We're going to need this later and + // forevermore, so we might as well get it out of the way upfront. if (vars->count > 0) { ct->envp = malloc(sizeof(char*) * vars->count + 1); @@ -82,15 +97,16 @@ static void complete_env(Crontab* ct) { idx++; } + // `execle` requires it to be NULL-terminated ct->envp[idx] = NULL; } } -array_t* get_filenames(char* dpath) { +array_t* get_filenames(char* dirpath) { DIR* dir; - if ((dir = opendir(dpath)) != NULL) { - printlogf("scanning dir %s\n", dpath); + if ((dir = opendir(dirpath)) != NULL) { + printlogf("scanning dir %s\n", dirpath); struct dirent* den; array_t* file_names = array_init(); @@ -101,7 +117,7 @@ array_t* get_filenames(char* dpath) { continue; } - printlogf("found file %s/%s\n", dpath, den->d_name); + printlogf("found file %s/%s\n", dirpath, den->d_name); array_push(file_names, s_copy(den->d_name)); } @@ -109,7 +125,7 @@ array_t* get_filenames(char* dpath) { return file_names; } else { perror("opendir"); - printlogf("unable to scan dir %s\n", dpath); + printlogf("unable to scan dir %s\n", dirpath); } return NULL; @@ -252,15 +268,15 @@ Crontab* new_crontab(int crontab_fd, bool is_root, time_t curr_time, void scan_crontabs(hash_table* old_db, hash_table* new_db, DirConfig dir_conf, time_t curr) { - array_t* fnames = get_filenames(dir_conf.name); + array_t* fnames = get_filenames(dir_conf.path); // If no files, fall through to db replacement // This will handle removal of any files that were deleted during runtime if (has_elements(fnames)) { foreach (fnames, i) { char* fname = array_get(fnames, i); char* fpath; - if (!(fpath = fmt_str("%s/%s", dir_conf.name, fname))) { - printlogf("failed to concatenate as %s/%s\n", dir_conf.name, fname); + if (!(fpath = fmt_str("%s/%s", dir_conf.path, fname))) { + printlogf("failed to concatenate as %s/%s\n", dir_conf.path, fname); continue; } Crontab* ct = (Crontab*)ht_get(old_db, fpath); diff --git a/src/crontab.h b/src/crontab.h index a479e37..23f3b8f 100644 --- a/src/crontab.h +++ b/src/crontab.h @@ -7,28 +7,95 @@ #include "libhash/libhash.h" #include "libutil/libutil.h" -// TODO: check dir mtime as well? +/** + * Holds metadata and configuration for one of the crontab directories being + * tracked. + * + * TODO: check dir mtime as well? + */ typedef struct { + /** + * Does this directory house system i.e. root crontabs? + */ bool is_root; - char *name; + /** + * The absolute path of the directory. + */ + char *path; } DirConfig; +/** + * Represents a single crontab (file). We assume (and we enforce this in the + * crontab program) each file will be named after its user, except for root/sys + * crontabs. + */ typedef struct { + /** + * The last time this crontab file was modified. + */ time_t mtime; + /** + * The owning user's username (and name of the crontab file). + */ char *uname; + /** + * An array of this crontab's entries. + */ array_t *entries; + /** + * A mapping of variables (key/value pairs) set in the crontab. + */ hash_table *vars; + /** + * A char* array of vars, concatenated such that each string is represented as + * "key=value" literals. + */ char **envp; } Crontab; -array_t *get_filenames(char *dpath); +/** + * Given a directory path, returns an array of all of the valid filenames + * therein. + * + * @param dirpath + */ +array_t *get_filenames(char *dirpath); +/** + * Parses a file into new crontab. + * + * @param crontab_fd The file descriptor for the crontab file. + * @param is_root A flag indicating whether this is a system (root-owned) + * crontab. + * @param curr_time The current time. + * @param mtime The last modified time of the file represented by `crontab_fd`. + * @param uname The username (and filename). + * @return Crontab* + */ Crontab *new_crontab(int crontab_fd, bool is_root, time_t curr_time, time_t mtime, char *uname); +/** + * Scans all crontabs in the given directory and updates them if needed. + * + * @param old_db A pointer to the current database. + * @param new_db A pointer to the new database; all updates will be placed here. + * @param dir_conf The dir config for the directory to be scanned. + * @param curr The current time. + * + * TODO: static + */ void scan_crontabs(hash_table *old_db, hash_table *new_db, DirConfig dir_conf, time_t curr); +/** + * Updates the crontab database by scanning all files that have been modified. + * + * @param db A pointer to the crontab database. + * @param curr The current time. + * @param dir_conf A variadic list of directories to be scanned. + * @param ... + */ void update_db(hash_table *db, time_t curr, DirConfig *dir_conf, ...); #endif /* FS_H */ diff --git a/src/daemon.h b/src/daemon.h index 2842d26..df70fb0 100644 --- a/src/daemon.h +++ b/src/daemon.h @@ -3,6 +3,11 @@ #include "util.h" +/** + * Daemonizes the current running process and detaches it from the TTY. + * + * @return Retval + */ Retval daemonize(); #endif /* DAEMON_H */ diff --git a/src/job.c b/src/job.c index 71da9c3..22b0851 100644 --- a/src/job.c +++ b/src/job.c @@ -5,7 +5,6 @@ #include #include #include -#include #include "constants.h" #include "cronentry.h" @@ -14,22 +13,19 @@ #include "opt-constants.h" #include "util.h" +// Mutex + cond var for the reaper daemon thread. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; -// need to free -static char* create_uuid(void) { - char uuid[UUID_STR_LEN]; - - uuid_t bin_uuid; - uuid_generate_random(bin_uuid); - uuid_unparse(bin_uuid, uuid); - - return s_copy(uuid); -} - -// TODO: pass entry by value? Otherwise if entry is freed before we call fork, -// we're fucked +/** + * Creates a new job of type CRON. + * + * @param entry The entry which this job will represent. + * @return Job* + * + * TODO: pass entry by value? Otherwise if entry is freed before we call + * fork, we're fucked. + */ static Job* new_cronjob(CronEntry* entry) { Job* job = xmalloc(sizeof(Job)); job->ret = -1; @@ -37,7 +33,7 @@ static Job* new_cronjob(CronEntry* entry) { job->state = PENDING; job->cmd = s_copy(entry->cmd); // TODO: free job->ident = create_uuid(); - job->type = CMD; + job->type = CRON; ht_record* r = ht_search(entry->parent->vars, MAILTO_ENVVAR); job->mailto = s_copy(r ? r->value : entry->parent->uname); @@ -45,6 +41,12 @@ static Job* new_cronjob(CronEntry* entry) { return job; } +/** + * Creates a new job of type MAIL. + * + * @param og_job The original job about which this MAIL job is reporting. + * @return Job* + */ static Job* new_mailjob(Job* og_job) { Job* job = xmalloc(sizeof(Job)); job->ret = -1; @@ -63,7 +65,17 @@ static Job* new_mailjob(Job* og_job) { return job; } -static bool await_job(pid_t pid, int* status) { +/** + * Checks for a return status on a RUNNING job's process. + * + * @param pid The process id to check. + * @param status An int pointer into which the job's return status will be + * stored, if applicable. + * @return true when the job has finished and the status pointer has been set. + * @return false when the job has not finished yet. The status pointer was + * unused (so don't use it!). + */ +static bool check_job(pid_t pid, int* status) { int r = waitpid(pid, status, WNOHANG); printlogf("[pid=%d] waitpid result is %d\n", pid, r); @@ -84,11 +96,14 @@ static bool await_job(pid_t pid, int* status) { static unsigned int temporary_mail_count = 0; // TODO: [bash] -// ps aux | grep './chronic' | grep -v 'grep' | awk '{print $2}' | wc -l -// ps aux | grep './chronic' | grep -v 'grep' | awk '{print $2}' | xargs kill -static void run_mailjob(Job* og_job) { - Job* job = new_mailjob(og_job); - array_push(job_queue, job); +/** + * Creates and runs a MAIL job to report the given EXITED job `exited_job`. + * + * @param exited_job The EXITED job to report in the MAIL job. + */ +static void run_mailjob(Job* exited_job) { + Job* job = new_mailjob(exited_job); + array_push(mail_queue, job); printlogf("[job %s] going to run mail cmd: %s\n", job->ident, job->cmd); @@ -110,17 +125,22 @@ static void run_mailjob(Job* og_job) { perror("pclose"); exit(EXIT_FAILURE); } + + exit(0); } + job->state = RUNNING; } -// void enqueue_job(CronEntry* entry) { -// Job* job = new_cronjob(entry); -// array_push(job_queue, job); -// } - -void run_cronjob(CronEntry* entry) { +/** + * Execute a cronjob for the given entry. + * @param entry + */ +static void run_cronjob(CronEntry* entry) { Job* job = new_cronjob(entry); + + pthread_mutex_lock(&mutex); array_push(job_queue, job); + pthread_mutex_unlock(&mutex); char* home = ht_get(entry->parent->vars, HOMEDIR_ENVVAR); char* shell = ht_get(entry->parent->vars, SHELL_ENVVAR); @@ -150,7 +170,6 @@ void run_cronjob(CronEntry* entry) { job->state = RUNNING; } -// ps aux | grep './chronic' | awk '{print $2}' | xargs kill static void reap_job(Job* job) { switch (job->state) { case PENDING: @@ -159,28 +178,31 @@ static void reap_job(Job* job) { break; case RUNNING: { int status; - if (await_job(job->pid, &status)) { + if (check_job(job->pid, &status)) { printlogf("[job %s] transition RUNNING->EXITED (pid=%d, status=%d\n", job->ident, job->pid, status); job->ret = status; job->state = EXITED; job->pid = -1; - - if (job->type == CMD) { - // run_mailjob(job); - } } } } } +/** + * Reaps RUNNING jobs and keeps the process space clean. + * + * @param _arg Mandatory (but thus far unused) void pointer thread argument + * @return void* Ditto ^ + */ static void* reap_routine(void* _arg) { while (true) { pthread_mutex_lock(&mutex); pthread_cond_wait(&cond, &mutex); printlogf("in reaper thread\n"); + foreach (job_queue, i) { Job* job = array_get(job_queue, i); // TODO: null checks everywhere ^ @@ -190,6 +212,20 @@ static void* reap_routine(void* _arg) { // we'll end up with zombley processes if (job->state == EXITED) { array_remove(job_queue, i); + if (job->type == CRON) { + run_mailjob(job); + } + } + } + + foreach (mail_queue, i) { + Job* job = array_get(mail_queue, i); + printf("\nCHECKING MAIL JOB\n\n"); + reap_job(job); + + if (job->state == EXITED) { + printlogf("[mail %s] finally exited\n", job->ident); + array_remove(mail_queue, i); } } @@ -213,3 +249,26 @@ void signal_reap_routine(void) { pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); } + +void run_jobs(hash_table* db, time_t ts) { + // We must iterate the capacity here because hash table records are not + // stored contiguously + if (db->count > 0) { + for (unsigned int i = 0; i < (unsigned int)db->capacity; i++) { + ht_record* r = db->records[i]; + + // If there's no record in this slot, continue + if (!r) { + continue; + } + + Crontab* ct = r->value; + foreach (ct->entries, i) { + CronEntry* entry = array_get(ct->entries, i); + if (entry->next == ts) { + run_cronjob(entry); + } + } + } + } +} diff --git a/src/job.h b/src/job.h index a39244b..15cb3fb 100644 --- a/src/job.h +++ b/src/job.h @@ -6,23 +6,90 @@ #include "cronentry.h" #include "libhash/libhash.h" -typedef enum { PENDING, RUNNING, EXITED } JobState; +/** + * Represents all possible job states. + */ +typedef enum { + /** + * The job is ready but has not been executed yet. + */ + PENDING, + /** + * The job has been executed but not awaited for a result. + */ + RUNNING, + /** + * The job has executed and its result has been collected. Any child processes + * associated with the job have been cleaned up. + */ + EXITED +} JobState; -typedef enum { CMD, MAIL } JobType; +/** + * Represents all possible job types. + */ +typedef enum { + /** + * The job represents a command specified in a crontab entry. + */ + CRON, + /** + * The job represents reporting that needs to be done regarding a completed + * cron job. + */ + MAIL +} JobType; +/** + * Represents a job executed by the daemon. + */ typedef struct { + /** + * A unique identifier for the job (UUID v4). + */ char *ident; + /** + * The command to be executed. + */ char *cmd; + /** + * The process id of the job when running. Starts as -1. + */ pid_t pid; + /** + * The current job state. + */ JobState state; + /** + * The username or email address to whom results will be reported. + * This is set by the MAILTO variable in the corresponding crontab. + * If MAILTO is not present, this will be set to the owning user's username. + */ char *mailto; + /** + * The job type. + */ JobType type; + /** + * The return status of the job, once executed. Starts as -1. + */ int ret; } Job; -void run_cronjob(CronEntry *entry); - +/** + * Initializes the job reap routine. This is a daemon thread that awaits job + * child processes and cleans them up. + * It works while the main daemon loop is paused for sleeping. + */ void init_reap_routine(void); + +/** + * Signals the reap routine to wake up and begin processing RUNNING state jobs. + * This encapsulates a condition variable that we leverage to ensure the reaper + * only does work while the main program is blocked and not doing anything + * useful. + */ void signal_reap_routine(void); +void run_jobs(hash_table *db, time_t ts); #endif /* JOB_H */ diff --git a/src/log.c b/src/log.c index 4e7de12..c58bca3 100644 --- a/src/log.c +++ b/src/log.c @@ -10,9 +10,8 @@ #include "constants.h" #include "libutil/libutil.h" +#include "opt-constants.h" -// TODO: Allow override -#define TIMESTAMP_FMT "%Y-%m-%d %H:%M:%S" #define LOG_HEADER TIMESTAMP_FMT " %%s " DAEMON_IDENT ": " static bool use_syslog = false; @@ -94,11 +93,3 @@ void logger_init(CliOptions *opts) { openlog(DAEMON_IDENT, LOG_CONS | LOG_PID, LOG_CRON); } } - -char *to_time_str(time_t t) { - char msg[512]; - struct tm *time_info = localtime(&t); - strftime(msg, sizeof(msg), TIMESTAMP_FMT, time_info); - - return fmt_str("%s", msg); -} diff --git a/src/log.h b/src/log.h index 559f93e..6c12a8c 100644 --- a/src/log.h +++ b/src/log.h @@ -5,8 +5,20 @@ #include "cli.h" +/** + * Initializes the logger using the CLI options to determine things such as + * where the log output goes. + * + * @param opts + */ void logger_init(CliOptions *opts); + +/** + * Printf but for logs! Prints to the log fd we've configured in `logger_init`. + * + * @param fmt The format string + * @param ... Everything else + */ void printlogf(const char *fmt, ...); -char *to_time_str(time_t t); #endif /* LOG_H */ diff --git a/src/main.c b/src/main.c index 49a5554..b54ca12 100644 --- a/src/main.c +++ b/src/main.c @@ -22,7 +22,7 @@ pid_t daemon_pid; char hostname[SMALL_BUFFER]; array_t* job_queue; -// array_t* mail_queue; +array_t* mail_queue; const char* job_state_names[] = {X(PENDING), X(RUNNING), X(EXITED)}; @@ -56,6 +56,7 @@ int main(int argc, char** argv) { logger_init(&opts); job_queue = array_init(); + mail_queue = array_init(); daemon_pid = getpid(); hash_table* db = ht_init(0); @@ -67,8 +68,8 @@ int main(int argc, char** argv) { time_t current_iter_time; - DirConfig sys = {.is_root = true, .name = SYS_CRONTABS_DIR}; - DirConfig usr = {.is_root = false, .name = CRONTABS_DIR}; + DirConfig sys = {.is_root = true, .path = SYS_CRONTABS_DIR}; + DirConfig usr = {.is_root = false, .path = CRONTABS_DIR}; update_db(db, start_time, &usr, NULL); // TODO: sys @@ -90,26 +91,7 @@ int main(int argc, char** argv) { printlogf("Current iter time: %s (rounded to %s)\n", to_time_str(current_iter_time), to_time_str(rounded_timestamp)); - // We must iterate the capacity here because hash table records are not - // stored contiguously - if (db->count > 0) { - for (unsigned int i = 0; i < (unsigned int)db->capacity; i++) { - ht_record* r = db->records[i]; - - // If there's no record in this slot, continue - if (!r) { - continue; - } - - Crontab* ct = r->value; - foreach (ct->entries, i) { - CronEntry* entry = array_get(ct->entries, i); - if (entry->next == rounded_timestamp) { - run_cronjob(entry); - } - } - } - } + run_jobs(db, rounded_timestamp); update_db(db, current_iter_time, &usr, NULL); } diff --git a/src/opt-constants.h b/src/opt-constants.h index 1bf6383..da2d645 100644 --- a/src/opt-constants.h +++ b/src/opt-constants.h @@ -17,4 +17,7 @@ #define MAILCMD_PATH "/usr/bin/mail" #endif +// TODO: Allow override +#define TIMESTAMP_FMT "%Y-%m-%d %H:%M:%S" + #endif /* OPT_CONSTANTS_H */ diff --git a/src/util.c b/src/util.c index 905ef15..4127e6e 100644 --- a/src/util.c +++ b/src/util.c @@ -1,7 +1,9 @@ #include +#include #include "libutil/libutil.h" #include "log.h" +#include "opt-constants.h" void* xmalloc(size_t sz) { void* ptr; @@ -12,3 +14,21 @@ void* xmalloc(size_t sz) { return ptr; } + +char* to_time_str(time_t t) { + char msg[512]; + struct tm* time_info = localtime(&t); + strftime(msg, sizeof(msg), TIMESTAMP_FMT, time_info); + + return fmt_str("%s", msg); +} + +char* create_uuid(void) { + char uuid[UUID_STR_LEN]; + + uuid_t bin_uuid; + uuid_generate_random(bin_uuid); + uuid_unparse(bin_uuid, uuid); + + return s_copy(uuid); +} diff --git a/src/util.h b/src/util.h index 4e89a0b..82398f1 100644 --- a/src/util.h +++ b/src/util.h @@ -26,4 +26,19 @@ inline static unsigned short get_sleep_duration(short interval, time_t now) { return sleep_time; } +/** + * Converts the given time_t into a stringified timestamp. + * + * @param t + * @return char* + */ +char *to_time_str(time_t ts); + +/** + * Generates a v4 UUID. + * + * TODO: need to free + */ +char *create_uuid(void); + #endif /* UTIL_H */ diff --git a/t/unit/crontab_test.c b/t/unit/crontab_test.c index 3acc16a..a2145e3 100644 --- a/t/unit/crontab_test.c +++ b/t/unit/crontab_test.c @@ -170,7 +170,7 @@ void scan_crontabs_test() { setup_test_file(usr_dirname, "user3", "* * * * * echo 'test3'\n"); DirConfig usr_dir; - usr_dir.name = usr_dirname; + usr_dir.path = usr_dirname; usr_dir.is_root = false; hash_table* db = ht_init(0); @@ -233,7 +233,7 @@ void update_db_test(void) { setup_test_file(usr_dirname, "user2", "* * * * * echo 'test2'\n"); DirConfig usr_dir; - usr_dir.name = usr_dirname; + usr_dir.path = usr_dirname; usr_dir.is_root = false; char* sys_dirname = setup_test_directory(); @@ -241,7 +241,7 @@ void update_db_test(void) { setup_test_file(sys_dirname, "root2", "* * * * * echo 'test2'\n"); DirConfig sys_dir; - sys_dir.name = sys_dirname; + sys_dir.path = sys_dirname; sys_dir.is_root = true; hash_table* db = ht_init(0); diff --git a/t/unit/main.c b/t/unit/main.c index 58b899e..7481eb2 100644 --- a/t/unit/main.c +++ b/t/unit/main.c @@ -8,35 +8,19 @@ CliOptions opts = {0}; char hostname[SMALL_BUFFER] = "unit_test"; -array_t* job_queue = NULL; -const char* job_state_names[] = {0}; -#include "libutil/libutil.h" -int main(int argc, char** argv) { - // plan(118); - // run_parser_tests(); - // run_regexpr_tests(); - // run_crontab_tests(); - // run_time_tests(); - - // done_testing(); +array_t* job_queue = NULL; +array_t* mail_queue = NULL; - array_t* arr = array_init(); +const char* job_state_names[] = {0}; - array_push(arr, "a"); - array_push(arr, "b"); - array_push(arr, "c"); +int main(int argc, char** argv) { + plan(118); - array_push(arr, "x"); - array_push(arr, "y"); - array_push(arr, "z"); + run_parser_tests(); + run_regexpr_tests(); + run_crontab_tests(); + run_time_tests(); - foreach (arr, i) { - char* v = array_get(arr, i); - if (s_equals(v, "x") || s_equals(v, "b")) { - printf("before rm %d\n", array_size(arr)); - array_remove(arr, i); - printf("after rm %d\n", array_size(arr)); - } - } + done_testing(); }