summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.c52
-rw-r--r--src/app.h14
-rw-r--r--src/html.c68
3 files changed, 81 insertions, 53 deletions
diff --git a/src/app.c b/src/app.c
index 28debe9..7d0df96 100644
--- a/src/app.c
+++ b/src/app.c
@@ -13,6 +13,28 @@
#include <sqlite3.h>
#include <fcntl.h>
+static const char *backup_status_db_strings[] = {
+ [STATUS_RUNNING] = "RUNNING",
+ [STATUS_FINISHED] = "FINISHED",
+ [STATUS_INTERRUPTED] = "INTERRUPTED",
+};
+
+const char *backup_status_to_db_string(enum BackupStatus status) {
+ assert(status >= 0 && status <= STATUS_INTERRUPTED);
+ return backup_status_db_strings[status];
+}
+
+enum BackupStatus backup_status_from_db_string(const char *str) {
+ assert(str != NULL);
+ for (int i = 0; i <= STATUS_INTERRUPTED; i++) {
+ if (strcmp(str, backup_status_db_strings[i]) == 0) {
+ return (enum BackupStatus)i;
+ }
+ }
+
+ abort();
+}
+
static void sqliteError(void *pArg, int iErrCode, const char *zMsg) {
// @CLEANUP: We should stuff these somewhere, but for now just print it
fprintf(stderr, "(%d) %s\n", iErrCode, zMsg);
@@ -305,7 +327,7 @@ int submit_report(struct App *app, char *client, char *secret, int is_finish) {
sqlite3_stmt *stmt;
// Mark any existing RUNNING backup as INTERRUPTED
- if(sqlite3_prepare_v2(conn, "UPDATE backups SET status = 'INTERRUPTED', completed = datetime(?1, 'unixepoch', 'subsec') WHERE client = ?2 AND status = 'RUNNING'", -1, &stmt, NULL) != SQLITE_OK) {
+ if(sqlite3_prepare_v2(conn, "UPDATE backups SET status = ?3, completed = datetime(?1, 'unixepoch', 'subsec') WHERE client = ?2 AND status = ?4", -1, &stmt, NULL) != SQLITE_OK) {
abort();
}
if(sqlite3_bind_double(stmt, 1, TIME_AS_FLOAT(now)) != SQLITE_OK) {
@@ -314,6 +336,12 @@ int submit_report(struct App *app, char *client, char *secret, int is_finish) {
if(sqlite3_bind_int64(stmt, 2, clientId) != SQLITE_OK) {
abort();
}
+ if(sqlite3_bind_text(stmt, 3, backup_status_to_db_string(STATUS_INTERRUPTED), -1, SQLITE_STATIC) != SQLITE_OK) {
+ abort();
+ }
+ if(sqlite3_bind_text(stmt, 4, backup_status_to_db_string(STATUS_RUNNING), -1, SQLITE_STATIC) != SQLITE_OK) {
+ abort();
+ }
if(sqlite3_step(stmt) != SQLITE_DONE) {
abort();
@@ -331,7 +359,7 @@ int submit_report(struct App *app, char *client, char *secret, int is_finish) {
if(sqlite3_bind_int64(stmt, 1, clientId) != SQLITE_OK) {
abort();
}
- if(sqlite3_bind_text(stmt, 2, "RUNNING", -1, SQLITE_STATIC) != SQLITE_OK) {
+ if(sqlite3_bind_text(stmt, 2, backup_status_to_db_string(STATUS_RUNNING), -1, SQLITE_STATIC) != SQLITE_OK) {
abort();
}
if(sqlite3_bind_double(stmt, 3, TIME_AS_FLOAT(now)) != SQLITE_OK) {
@@ -350,12 +378,15 @@ int submit_report(struct App *app, char *client, char *secret, int is_finish) {
uint64_t backupId;
sqlite3_stmt *stmt;
- if(sqlite3_prepare_v2(conn, "SELECT id FROM backups WHERE client = ?1 AND status = 'RUNNING'", -1, &stmt, NULL) != SQLITE_OK) {
+ if(sqlite3_prepare_v2(conn, "SELECT id FROM backups WHERE client = ?1 AND status = ?2", -1, &stmt, NULL) != SQLITE_OK) {
abort();
}
if(sqlite3_bind_int64(stmt, 1, clientId) != SQLITE_OK) {
abort();
}
+ if(sqlite3_bind_text(stmt, 2, backup_status_to_db_string(STATUS_RUNNING), -1, SQLITE_STATIC) != SQLITE_OK) {
+ abort();
+ }
if(sqlite3_step(stmt) != SQLITE_ROW) {
abort();
@@ -378,7 +409,7 @@ int submit_report(struct App *app, char *client, char *secret, int is_finish) {
if(sqlite3_prepare_v2(conn, "UPDATE backups SET status = ?1, completed = datetime(?2, 'unixepoch', 'subsec') WHERE id = ?3", -1, &stmt, NULL) != SQLITE_OK) {
abort();
}
- if(sqlite3_bind_text(stmt, 1, "FINISHED", -1, SQLITE_STATIC) != SQLITE_OK) {
+ if(sqlite3_bind_text(stmt, 1, backup_status_to_db_string(STATUS_FINISHED), -1, SQLITE_STATIC) != SQLITE_OK) {
abort();
}
@@ -517,16 +548,11 @@ int list_clients(struct App *app, struct ListClientResult *result) {
result->clients[index].last_backup = time_from_double(last_backup);
if (backup_status == NULL) {
- strncpy(result->clients[index].status, "NEVER", BACKUP_STATUS_MAX);
- result->clients[index].is_stale = 0;
- } else if (strcmp((char*)backup_status, "RUNNING") == 0) {
- strncpy(result->clients[index].status, "RUNNING", BACKUP_STATUS_MAX);
- result->clients[index].is_stale = 0;
- } else if (strcmp((char*)backup_status, "INTERRUPTED") == 0) {
- strncpy(result->clients[index].status, "INTERRUPTED", BACKUP_STATUS_MAX);
+ result->clients[index].last_backup.tv_sec = 0;
+ result->clients[index].status = 0;
result->clients[index].is_stale = 0;
} else {
- strncpy(result->clients[index].status, "OK", BACKUP_STATUS_MAX);
+ result->clients[index].status = backup_status_from_db_string((char*)backup_status);
result->clients[index].is_stale = 0;
}
@@ -619,7 +645,7 @@ int get_client_backups(struct App *app, int64_t client_id, struct GetBackupsResu
const double started = sqlite3_column_double(stmt, 2);
const double completed = sqlite3_column_double(stmt, 3);
- strncpy(result->backups[index].status, status ? (char*)status : "", BACKUP_STATUS_MAX);
+ result->backups[index].status = backup_status_from_db_string(status ? (char*)status : NULL);
result->backups[index].started = time_from_double(started);
result->backups[index].completed = time_from_double(completed);
diff --git a/src/app.h b/src/app.h
index 46d2c18..14bc052 100644
--- a/src/app.h
+++ b/src/app.h
@@ -6,9 +6,17 @@
#define CLIENT_NAME_MAX 32
#define CLIENT_SECRET_LEN 33
-#define BACKUP_STATUS_MAX 16
#define MAX_BACKUPS_DISPLAY 50
+enum BackupStatus {
+ STATUS_RUNNING,
+ STATUS_FINISHED,
+ STATUS_INTERRUPTED,
+};
+
+const char *backup_status_to_db_string(enum BackupStatus status);
+enum BackupStatus backup_status_from_db_string(const char *str);
+
struct App {
const char* dbname;
char session_secret[CLIENT_SECRET_LEN];
@@ -32,7 +40,7 @@ struct ListClientResultClient {
char name[CLIENT_NAME_MAX];
char secret[CLIENT_SECRET_LEN];
struct timespec last_backup;
- char status[BACKUP_STATUS_MAX]; // "OK", "RUNNING", "STALE", "FAILED", "INTERRUPTED"
+ enum BackupStatus status;
int is_stale;
};
@@ -46,7 +54,7 @@ int list_clients(struct App *app, struct ListClientResult *result);
struct BackupRecord {
int64_t id;
- char status[BACKUP_STATUS_MAX];
+ enum BackupStatus status;
struct timespec started;
struct timespec completed;
};
diff --git a/src/html.c b/src/html.c
index b7fd4de..a848ebe 100644
--- a/src/html.c
+++ b/src/html.c
@@ -2,6 +2,7 @@
#include "mytime.h"
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
@@ -178,7 +179,6 @@ static void emit_stylesheet(FILE *f) {
" .clients > a.stale { border-left: 3px solid var(--purple); padding-left: 11px; }\n"
" .clients > a.stale .client-name { border-left: none; padding-left: 0; }\n"
"}\n"
- "/* BADGES */\n"
".badge { border: 1px solid; padding: 2px 7px; font-size: 10px; letter-spacing: 1px; justify-self: center; }\n"
".badge.ok { color: var(--green); background: var(--green-bg); border-color: var(--green-bor); }\n"
".badge.run { color: var(--yellow); background: var(--yellow-bg); border-color: var(--yellow-bor); }\n"
@@ -282,7 +282,7 @@ static void emit_stylesheet(FILE *f) {
" gap: 12px;\n"
"}\n"
".backups > div > time { color: var(--hi); font-size: 13px; flex: 1; }\n"
- ".backups > div > span { color: var(--dim); font-size: 12px; min-width: 70px; text-align: right; }\n"
+ ".backups > div > .duration { color: var(--dim); font-size: 12px; min-width: 70px; text-align: right; }\n"
"</style>\n"
);
}
@@ -298,22 +298,24 @@ static void mask_secret(const char *secret, char *out, size_t out_len) {
}
}
-static const char *status_to_badge_class(const char *status) {
- if (strcmp(status, "OK") == 0) return "ok";
- if (strcmp(status, "RUNNING") == 0) return "run";
- if (strcmp(status, "STALE") == 0) return "stale";
- if (strcmp(status, "INTERRUPTED") == 0) return "fail";
- if (strcmp(status, "FINISHED") == 0) return "ok";
- return "";
+static const char *status_to_badge_class(enum BackupStatus status) {
+ switch (status) {
+ case STATUS_RUNNING: return "run";
+ case STATUS_INTERRUPTED: return "fail";
+ case STATUS_FINISHED: return "ok";
+ }
+
+ abort();
}
-static const char *status_to_display(const char *status) {
- if (strcmp(status, "OK") == 0) return "OK";
- if (strcmp(status, "RUNNING") == 0) return "Running";
- if (strcmp(status, "STALE") == 0) return "Stale";
- if (strcmp(status, "INTERRUPTED") == 0) return "Failed";
- if (strcmp(status, "FINISHED") == 0) return "Done";
- return status;
+static const char *status_to_display(enum BackupStatus status) {
+ switch (status) {
+ case STATUS_RUNNING: return "Running";
+ case STATUS_INTERRUPTED: return "Failed";
+ case STATUS_FINISHED: return "Done";
+ }
+
+ abort();
}
char *html_render_clients_page(const struct Request *request, const struct ListClientResult *result, size_t *out_len) {
@@ -386,7 +388,7 @@ char *html_render_clients_page(const struct Request *request, const struct ListC
" <span class=\"client-name\">%s</span>\n"
" <code class=\"client-secret\">%s</code>\n"
" <span class=\"client-time\">%s</span>\n"
- " <b class=\"badge client-badge %s\">%s</b>\n"
+ " <span class=\"badge client-badge %s\">%s</span>\n"
" <span class=\"client-action\">View &rarr;</span>\n"
" </a>\n",
result->clients[i].id,
@@ -413,13 +415,13 @@ char *html_render_clients_page(const struct Request *request, const struct ListC
char *html_render_client_detail_page(const struct Request *request, const struct GetClientOnlyResult *client_result, const struct GetBackupsResult *backups_result, size_t *out_len) {
// Determine status and staleness
int is_stale = 0;
- const char *current_status = "OK";
+ enum BackupStatus current_status = 0;
if (backups_result->backups_len > 0) {
- const char *last_status = backups_result->backups[0].status;
- if (strcmp(last_status, "RUNNING") == 0) {
- current_status = "RUNNING";
- } else if (strcmp(last_status, "INTERRUPTED") == 0) {
- current_status = "INTERRUPTED";
+ enum BackupStatus last_status = backups_result->backups[0].status;
+ if (last_status == STATUS_RUNNING) {
+ current_status = STATUS_RUNNING;
+ } else if (last_status == STATUS_INTERRUPTED) {
+ current_status = STATUS_INTERRUPTED;
}
}
@@ -449,7 +451,7 @@ char *html_render_client_detail_page(const struct Request *request, const struct
" <a href=\"/clients\">&larr; Back to clients</a>\n"
" <article%s>\n"
" <h3>%s</h3>\n"
- " <p>%d backups <b class=\"badge %s\">%s</b></p>\n",
+ " <p>%d backups <span class=\"badge %s\">%s</span></p>\n",
is_stale ? " class=\"stale\"" : "",
client_result->name,
backups_result->backups_len,
@@ -485,34 +487,26 @@ char *html_render_client_detail_page(const struct Request *request, const struct
fprintf(f, " <h4>Recent Backups</h4>\n");
if(backups_result->backups_len == 0) {
+ // @CLEANUP: Remove the inline style here
fprintf(f, " <p style=\"color:var(--dim)\">No backups recorded.</p>\n");
} else {
fprintf(f, " <div class=\"backups\">\n");
for (int i = 0; i < backups_result->backups_len; i++) {
- const char *badge_class;
- const char *badge_text;
bool duration_valid = false;
- if (strcmp(backups_result->backups[i].status, "RUNNING") == 0) {
- badge_class = "run";
- badge_text = "Running";
- } else if (strcmp(backups_result->backups[i].status, "INTERRUPTED") == 0) {
+ if (backups_result->backups[i].status == STATUS_INTERRUPTED) {
duration_valid = true;
- badge_class = "fail";
- badge_text = "Failed";
} else {
duration_valid = true;
- badge_class = "ok";
- badge_text = "Done";
}
struct timespec duration = time_sub(&backups_result->backups[i].completed, &backups_result->backups[i].started);
fprintf(f,
- " <div><time>%s</time><span>%s</span><b class=\"badge %s\">%s</b></div>\n",
+ " <div><time>%s</time><time class=\"duration\">%s</time><span class=\"badge %s\">%s</span></div>\n",
format_time(backups_result->backups[i].started).buf,
duration_valid ? format_duration(duration).buf : "",
- badge_class,
- badge_text
+ status_to_badge_class(backups_result->backups[i].status),
+ status_to_display(backups_result->backups[i].status)
);
}