Added support for backup using rsync

This commit is contained in:
Tomas Rivera
2026-04-06 13:50:37 +02:00
parent 8da1a430d7
commit 5d3e406d18
6 changed files with 256 additions and 23 deletions
+46 -2
View File
@@ -77,7 +77,7 @@ int main(int argc, char *argv[]) {
char *notesDirectoryString = malloc(PATH_MAX);
if (dirJson && cJSON_IsString(dirJson) && dirJson->valuestring != NULL) {
char *rawPath = dirJson->valuestring;
if (rawPath[0] == '$') { // expands $ in the path
if (rawPath[0] == '~') { // expands ~ in the path
snprintf(notesDirectoryString, PATH_MAX, "%s/%s", homedir, rawPath+1);
} else {
notesDirectoryString = rawPath;
@@ -131,6 +131,46 @@ int main(int argc, char *argv[]) {
newLineOnOpening = cJSON_IsTrue(newLineOnOpeningJSON) ? 1 : 0;
} else {debug("config.json did not contained a correct \"newLineOnOpening\" value. The default is true.");}
int doesBackup = 0;
int interval = 0; // this is an int. But some times it will be inputed a string. We must translate it.
char *pathToBackup = malloc(PATH_MAX);
cJSON *backupJSON = cJSON_GetObjectItem(json, "backup");
if (backupJSON && cJSON_IsObject(backupJSON)) {
cJSON *doesBackupJSON = cJSON_GetObjectItem(backupJSON, "enable");
if (doesBackupJSON && cJSON_IsBool(doesBackupJSON)) {
doesBackup = cJSON_IsTrue(doesBackupJSON) ? 1 : 0;
debug("doesBackup is set to %d", doesBackup);
cJSON *pathToBackupJSON = cJSON_GetObjectItem(backupJSON, "directory");
if (pathToBackupJSON && cJSON_IsString(pathToBackupJSON)) {
if (pathToBackupJSON->valuestring[0] == '~') { // expands ~
char *temp = pathToBackupJSON->valuestring;
temp++;
snprintf(pathToBackup, PATH_MAX, "%s/%s", homedir, temp);
debug("~ was expanded to %s\nThe backup path is %s", homedir, pathToBackup);
} else {
pathToBackup = strndup(pathToBackupJSON->valuestring, PATH_MAX);
debug("Directory for backup is set to %s in config.json", pathToBackup); // (TODO LATER) Maybe when in debug message when we say config.json get the actual config name.
}
} else {error(1, "user", "config.json did not contained a directory inside the backup section or the value is from an unexpected type");}
cJSON *intervalJSON = cJSON_GetObjectItem(backupJSON, "interval");
if (intervalJSON && cJSON_IsString(intervalJSON)) {
if (strcmp(intervalJSON->valuestring, "daily") == 0) {
interval = DAILY;
debug("interval in config.json is set to \"daily\" which is %d", DAILY);
} else if (strcmp(intervalJSON->valuestring, "weekly") == 0) {
interval = WEEKLY;
debug("interval in config.json is set to \"weekly\" which is %d", WEEKLY);
} else if (strcmp(intervalJSON->valuestring, "monthly") == 0) {
interval = MONTHLY;
debug("interval in config.json is set to \"monthly\" which is %d", MONTHLY);
} else {error(1, "user", "Unexpected string %s for entry \"interval\" in config.json. You must put an int (number of seconds) or \"daily\" or \"weekly\" or \"monthly\".", intervalJSON->valuestring);}
} else if (intervalJSON && cJSON_IsNumber(intervalJSON)) {
interval = intervalJSON->valueint; // (TODO LATER) "writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead". we should pass all of these JSON->value... to the designed functions
debug("interval in config.json is set to %d", interval);
} else {error(1, "user", "config.json did not contained an interval value inside the backup section or the value is from an unexpected type");}
} else{error(1, "user", "config.json did not contained a enable value inside the backup section or the value is from an unexpected type");}
} else {debug("config.json did not contained a correct \"backup\" section. The default is {\"enable\": false}");}
//cleans up
cJSON_Delete(json);
free(data);
@@ -158,7 +198,7 @@ int main(int argc, char *argv[]) {
printf(" -e, --editor Specify the editor to open.\n");
printf(" -j, --jump Jumps to the end of the file on opening.\n");
printf(" -J, --no-jump Do not jump to the end of the file\n");
printf(" -n, --note <note's name> Specify the note (or journal).");
printf(" -n, --note <note's name> Specify the note (or journal).\n");
printf(" -r, --render Renders the note with Vivify.\n");
printf(" -R, --no-render Do not render.\n");
printf(" -v, --vault <vault's name> Specify the vault.\n");
@@ -202,6 +242,10 @@ int main(int argc, char *argv[]) {
error(!doesEditorExist(editorToOpen, shouldDebug), "user", "%s is either not in your path or not installed.", editorToOpen);
debug("Finished parsing the attribute flags");
if (doesBackup) { // (TODO LATER) If this takes too much time we should add a warning and when implementing multiple directories for vault we should verifiy this works.
handleBackups(notesDirectoryString, pathToBackup, homedir, interval, shouldDebug);
}
int shouldExit = 0;
while(!shouldExit) {
// this loop is the vault selector
+123
View File
@@ -1,4 +1,5 @@
#include "utils.h"
#include <stdio.h>
const char *supportedEditor[] = {"neovim", "vim", "nano"};
const int numEditors = 3;
@@ -49,6 +50,128 @@ void _error(const int shouldDebug, const int condition, const char *type, const
}
}
void copyDir(const char *source, const char *destination, const int shouldDebug) {
debug("Backuping... source: %s and destination: %s", source, destination);
pid_t pid = fork();
error(pid < 0, "program", "fork() faild. pid = %d", pid);
if (pid == 0) {
// Child process: execute rsync
char *args[] = { // (TODO LATER)
"rsync",
"-Lqah", // archive, follow links, human-readable // (TODO LATER) add a way to get verbose (-v) without it going on top of ncurses
"--progress", // show progress
"--update", // only update if newer
(char *)source,
(char *)destination,
NULL
};
execvp("rsync", args);
// If execvp returns, there was an error
error(1, "program", "execvp() failed");
} else {
debug("Backup started asynchronously with PID %d\n", pid);
}
}
void handleBackups(const char *pathOfVaults, const char *pathOfBackup, const char *homeDir, const int interval, const int shouldDebug) {
int shouldBackup = 0;
time_t now = time(NULL);
debug("Time since epoch is %ld", (long)now);
char cacheFilePATH[PATH_MAX];
snprintf(cacheFilePATH, PATH_MAX, "%s/.cache/notewrapper/backupTime.txt", homeDir);
struct stat st;
if (stat(cacheFilePATH, &st) != 0) { // file does not exist
shouldBackup = 1;
FILE *cacheFile = fopen(cacheFilePATH, "w");
if (!cacheFile) {
error(1, "program", "Failed to open cache file for writing: %s", cacheFilePATH);
}
fprintf(cacheFile, "%ld", (long)now);
fclose(cacheFile);
} else {
FILE *cacheFile = fopen(cacheFilePATH, "r");
if (!cacheFile) {
error(1, "program", "Failed to open cache file for reading: %s", cacheFilePATH);
}
char line[64];
if (fgets(line, sizeof(line), cacheFile) == NULL) {
error(1, "program", "Failed to read backup cache file (at %s)", cacheFilePATH);
}
fclose(cacheFile);
char *endptr;
long lastBackupTime = strtol(line, &endptr, 10);
if (endptr == line || (*endptr != '\n' && *endptr != '\0')) {
error(1, "program", "Invalid timestamp in cache file: %s", line);
}
if (difftime(now, lastBackupTime) > interval) {
shouldBackup = 1;
cacheFile = fopen(cacheFilePATH, "w");
if (!cacheFile) {
error(1, "program", "Failed to open cache file for writing");
}
fprintf(cacheFile, "%ld\n", (long)now);
fclose(cacheFile);
}
}
if (shouldBackup) {
copyDir(pathOfVaults, pathOfBackup, shouldDebug);
}
}
/*void handleBackups(const char *pathOfVaults, const char *pathOfBackup, const char *homeDir, const int interval, const int shouldDebug) {
int shouldBackup = 0;
time_t now = time(NULL);
debug("Time since epoch is %ld", (long)now);
char cacheFilePATH[PATH_MAX];
// we store in this file the epoch time of the last backup to see if we need to backup
snprintf(cacheFilePATH, PATH_MAX, "%s/.cache/notewrapper/backupTime.txt", homeDir);
FILE *cacheFile = fopen(cacheFilePATH, "r");
if (errno == ENOENT) { // if the files does not exists. We backup and set the backup time to now
shouldBackup = 1;
fclose(cacheFile);
cacheFile = fopen(cacheFilePATH, "w");
fprintf(cacheFile, "%ld", (long)now);
fclose(cacheFile);
} else {
char line[64];
if (fgets(line, sizeof(line), cacheFile) == NULL) {
// Error reading the file
error(1, "program", "Failed to read backup cache file (at %s)", cacheFilePATH);
fclose(cacheFile);
return;
}
fclose(cacheFile);
// Convert the read line to long
char *endptr;
long lastBackupTime = strtol(line, &endptr, 10);
if (endptr == line || (*endptr != '\n' && *endptr != '\0')) {
error(1, "program", "Invalid timestamp in cache file: %s", line);
return;
}
if (difftime(now, lastBackupTime) > interval) {
shouldBackup = 1;
// Update the cache file with current time
cacheFile = fopen(cacheFilePATH, "w");
if (cacheFile == NULL) {
error(1, "program", "Failed to open cache file for writing");
return;
}
fprintf(cacheFile, "%ld\n", (long)now);
fclose(cacheFile);
}
}
if (shouldBackup) {
copyDir(pathOfVaults, pathOfBackup, shouldDebug);
}
}*/
int doesEditorExist (char *editorToCheck, int shouldDebug) { // Some exectuables have not exaclty the same name as the editor.
char *editor;
+7
View File
@@ -18,6 +18,10 @@
#include <errno.h>
#include <time.h>
#define BUFFER_SIZE 256 //standard buffer size.
// the three supported values are "daily" "weekly" and "monthly" (for months we use the avarage lenght of a month in a non leap year). All values were calculated on my loyal TI-30 ECO RS
#define DAILY 86400
#define WEEKLY 604800
#define MONTHLY 2628000 // calculated from the average lenght of a non leap year
#define debug(message, ...) \
_debug(shouldDebug, __FILE__, __LINE__, __func__, message, ##__VA_ARGS__)
#define altDebug(message, ...) \
@@ -67,4 +71,7 @@ int openEditor(char *path, char *editor, int render, int shouldJumpToEndOfFile,
// returns a string (buffered at 256 chars).
// you should not forgot to free this string as it is in the heap.
char *getFormatedTime(char *format, int shouldDebug);
/* Caclulates if we need to do another backup (by reading ~/.cache/NoteWrapper/backupTime.txt.
If need launches in the background rsync to do the backuping.*/
void handleBackups(const char *pathOfVaults, const char *pathOfBackup, const char *homeDir, const int interval, const int shouldDebug);
#endif