cinera.c: Gracefully handle database non-creation

This commit is contained in:
Matt Mascarenhas 2020-06-24 17:34:41 +01:00
parent b3470e0f48
commit 6eeb588adf
1 changed files with 111 additions and 97 deletions

View File

@ -23,7 +23,7 @@ typedef struct
version CINERA_APP_VERSION = {
.Major = 0,
.Minor = 7,
.Patch = 17
.Patch = 18
};
#include <stdarg.h> // NOTE(matt): varargs
@ -4735,7 +4735,6 @@ void *
LocateBlock(block_id BlockID)
{
void *Result = 0;
db_header *Header = (db_header *)DB.Metadata.File.Buffer.Location;
char *Ptr = (char *)Header;
Ptr += sizeof(db_header);
@ -14133,9 +14132,10 @@ WriteEntireDatabase()
fclose(DB.Metadata.File.Handle);
}
int
rc
InitDB(void)
{
rc Result = RC_SUCCESS;
// TODO(matt): InitDB() is called once on startup. This is correct for the .metadata file, because we only want one of
// those to house the info for all projects. However, we will want to create a .index file for each project, so
// need a separate InitIndex() function that we can call when looping over the projects after ParseConfig()
@ -14261,7 +14261,7 @@ InitDB(void)
LogError(LOG_ERROR, "Unable to create directory %.*s: %s", (int)Config->DatabaseLocation.Length, Config->DatabaseLocation.Base, strerror(errno));
fprintf(stderr, "Unable to create directory %.*s: %s\n", (int)Config->DatabaseLocation.Length, Config->DatabaseLocation.Base, strerror(errno));
Free(DatabaseLocation0);
return RC_ERROR_DIRECTORY;
Result = RC_ERROR_DIRECTORY;
};
}
else
@ -14271,39 +14271,43 @@ InitDB(void)
}
Free(DatabaseLocation0);
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
if(DB.Metadata.File.Handle)
if(Result == RC_SUCCESS)
{
fwrite(&DB.Header, sizeof(DB.Header), 1, DB.Metadata.File.Handle);
DB.Metadata.Signposts.ProjectsBlock.Byte = ftell(DB.Metadata.File.Handle);
fwrite(&DB.ProjectsBlock, sizeof(DB.ProjectsBlock), 1, DB.Metadata.File.Handle);
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
if(DB.Metadata.File.Handle)
{
fwrite(&DB.Header, sizeof(DB.Header), 1, DB.Metadata.File.Handle);
DB.Metadata.Signposts.AssetsBlock.Byte = ftell(DB.Metadata.File.Handle);
fwrite(&DB.AssetsBlock, sizeof(DB.AssetsBlock), 1, DB.Metadata.File.Handle);
DB.Metadata.Signposts.ProjectsBlock.Byte = ftell(DB.Metadata.File.Handle);
fwrite(&DB.ProjectsBlock, sizeof(DB.ProjectsBlock), 1, DB.Metadata.File.Handle);
fclose(DB.Metadata.File.Handle);
ReadFileIntoBuffer(&DB.Metadata.File);
DB.Metadata.Signposts.ProjectsBlock.Ptr = DB.Metadata.File.Buffer.Location + DB.Metadata.Signposts.ProjectsBlock.Byte;
DB.Metadata.Signposts.AssetsBlock.Ptr = DB.Metadata.File.Buffer.Location + DB.Metadata.Signposts.AssetsBlock.Byte;
}
else
{
// TODO(matt): Handle unopenable database files
PrintC(CS_RED, "Could not open database file: ");
PrintC(CS_MAGENTA_BOLD, DB.Metadata.File.Path);
fprintf(stderr, "\n");
_exit(0);
}
DB.Metadata.Signposts.AssetsBlock.Byte = ftell(DB.Metadata.File.Handle);
fwrite(&DB.AssetsBlock, sizeof(DB.AssetsBlock), 1, DB.Metadata.File.Handle);
fclose(DB.Metadata.File.Handle);
ReadFileIntoBuffer(&DB.Metadata.File);
DB.Metadata.Signposts.ProjectsBlock.Ptr = DB.Metadata.File.Buffer.Location + DB.Metadata.Signposts.ProjectsBlock.Byte;
DB.Metadata.Signposts.AssetsBlock.Ptr = DB.Metadata.File.Buffer.Location + DB.Metadata.Signposts.AssetsBlock.Byte;
}
else
{
// TODO(matt): Handle unopenable database files
PrintC(CS_RED, "Could not open database file: ");
PrintC(CS_MAGENTA_BOLD, DB.Metadata.File.Path);
fprintf(stderr, "\n");
_exit(0);
}
#if 0
DB.File.Handle = fopen(DB.File.Path, "w");
fprintf(DB.File.Handle, "---\n");
fclose(DB.File.Handle);
ReadFileIntoBuffer(&DB.File);
DB.File.Handle = fopen(DB.File.Path, "w");
fprintf(DB.File.Handle, "---\n");
fclose(DB.File.Handle);
ReadFileIntoBuffer(&DB.File);
#endif
}
}
return RC_SUCCESS;
return Result;
}
void
@ -15340,58 +15344,63 @@ PrintEvent(struct inotify_event *Event, int EventIndex, int Indentation)
}
#endif
void
rc
InitAll(neighbourhood *Neighbourhood, buffers *CollationBuffers, template *BespokeTemplate)
{
rc Result = RC_SUCCESS;
MEM_TEST_TOP("InitAll()");
RewindCollationBuffers(CollationBuffers);
/* */ MEM_TEST_MID("InitAll()");
/* +MEM */ InitDB();
/* +MEM */ Result = InitDB();
/* */ MEM_TEST_MID("InitAll()");
// TODO(matt): Straight up remove these PrintAssetsBlock() calls
//PrintAssetsBlock(0);
SyncDB(Config);
//PrintAssetsBlock(0);
printf("\n╾─ Hashing assets ─╼\n");
// NOTE(matt): This had to happen before PackTemplate() because those guys may need to do PushAsset() and we must
// ensure that the builtin assets get placed correctly
InitAssets();
PushConfiguredAssets();
//
////
PackTemplates(Neighbourhood);
bool TitledSync = FALSE;
for(int i = 0; i < Config->Project.ItemCount; ++i)
if(Result == RC_SUCCESS)
{
/* */ MEM_TEST_MID("InitAll()");
/* +MEM */ SyncProject(GetPlaceInBook(&Config->Project, i), Neighbourhood, CollationBuffers, BespokeTemplate, &TitledSync);
/* */ MEM_TEST_MID("InitAll()");
// TODO(matt): Straight up remove these PrintAssetsBlock() calls
//PrintAssetsBlock(0);
SyncDB(Config);
//PrintAssetsBlock(0);
printf("\n╾─ Hashing assets ─╼\n");
// NOTE(matt): This had to happen before PackTemplate() because those guys may need to do PushAsset() and we must
// ensure that the builtin assets get placed correctly
InitAssets();
PushConfiguredAssets();
//
////
PackTemplates(Neighbourhood);
bool TitledSync = FALSE;
for(int i = 0; i < Config->Project.ItemCount; ++i)
{
/* */ MEM_TEST_MID("InitAll()");
/* +MEM */ SyncProject(GetPlaceInBook(&Config->Project, i), Neighbourhood, CollationBuffers, BespokeTemplate, &TitledSync);
/* */ MEM_TEST_MID("InitAll()");
}
SyncGlobalPagesWithInput(Neighbourhood, CollationBuffers);
for(int i = 0; i < Assets.ItemCount; ++i)
{
UpdateAssetInDB(GetPlaceInBook(&Assets, i));
}
DeleteStaleAssets();
//PrintAssetsBlock(0);
MEM_TEST_END("InitAll()");
fprintf(stderr,
"\n"
"╾─ Monitoring file system for %snew%s, %sedited%s and %sdeleted%s .hmml and asset files ─╼\n",
ColourStrings[EditTypes[EDIT_ADDITION].Colour], ColourStrings[CS_END],
ColourStrings[EditTypes[EDIT_REINSERTION].Colour], ColourStrings[CS_END],
ColourStrings[EditTypes[EDIT_DELETION].Colour], ColourStrings[CS_END]);
}
SyncGlobalPagesWithInput(Neighbourhood, CollationBuffers);
for(int i = 0; i < Assets.ItemCount; ++i)
{
UpdateAssetInDB(GetPlaceInBook(&Assets, i));
}
DeleteStaleAssets();
//PrintAssetsBlock(0);
MEM_TEST_END("InitAll()");
fprintf(stderr,
"\n"
"╾─ Monitoring file system for %snew%s, %sedited%s and %sdeleted%s .hmml and asset files ─╼\n",
ColourStrings[EditTypes[EDIT_ADDITION].Colour], ColourStrings[CS_END],
ColourStrings[EditTypes[EDIT_REINSERTION].Colour], ColourStrings[CS_END],
ColourStrings[EditTypes[EDIT_DELETION].Colour], ColourStrings[CS_END]);
return Result;
}
void
@ -15672,6 +15681,7 @@ void
Exit(void)
{
Free(MemoryArena.Location);
fprintf(stderr, "Exiting\n");
_exit(0);
}
@ -15812,6 +15822,7 @@ main(int ArgC, char **Args)
PushWatchHandle(ConfigPathL, EXT_NULL, WT_CONFIG, 0, 0);
Config = ParseConfig(ConfigPathL, &TokensList);
rc Succeeding = RC_SUCCESS;
if(Config)
{
if(Mode & MODE_EXAMINE)
@ -15828,7 +15839,7 @@ main(int ArgC, char **Args)
else
{
/* */ MEM_TEST_MID("main()");
/* +MEM */ InitAll(&Neighbourhood, &CollationBuffers, &BespokeTemplate);
/* +MEM */ Succeeding = InitAll(&Neighbourhood, &CollationBuffers, &BespokeTemplate);
/* */ MEM_TEST_MID("main()");
}
}
@ -15844,36 +15855,39 @@ main(int ArgC, char **Args)
}
}
if(inotifyInstance)
if(Succeeding == RC_SUCCESS)
{
while(MonitorFilesystem(&Neighbourhood, &CollationBuffers, &BespokeTemplate, ConfigPathL, &TokensList) != RC_ARENA_FULL)
if(inotifyInstance)
{
// TODO(matt): Refetch the quotes and rebuild player pages if needed
//
// Every sixty mins, redownload the quotes and, I suppose, SyncDBWithInput(). But here we still don't even know
// who the speaker is. To know, we'll probably have to store all quoted speakers in the project's .metadata. Maybe
// postpone this for now, but we will certainly need this to happen
//
// The most ideal solution is possibly that we store quote numbers in the Metadata->Entry, listen for and handle a
// REST PUT request from insobot when a quote changes (unless we're supposed to poll insobot for them?), and rebuild
// the player page(s) accordingly.
//
if(!(Mode & MODE_DRYRUN) && Config && Config->RespectingPrivacy && time(0) - LastPrivacyCheck > Config->PrivacyCheckInterval)
while(MonitorFilesystem(&Neighbourhood, &CollationBuffers, &BespokeTemplate, ConfigPathL, &TokensList) != RC_ARENA_FULL)
{
RecheckPrivacy(&Neighbourhood, &CollationBuffers, &BespokeTemplate);
// TODO(matt): Refetch the quotes and rebuild player pages if needed
//
// Every sixty mins, redownload the quotes and, I suppose, SyncDBWithInput(). But here we still don't even know
// who the speaker is. To know, we'll probably have to store all quoted speakers in the project's .metadata. Maybe
// postpone this for now, but we will certainly need this to happen
//
// The most ideal solution is possibly that we store quote numbers in the Metadata->Entry, listen for and handle a
// REST PUT request from insobot when a quote changes (unless we're supposed to poll insobot for them?), and rebuild
// the player page(s) accordingly.
//
if(!(Mode & MODE_DRYRUN) && Config && Config->RespectingPrivacy && time(0) - LastPrivacyCheck > Config->PrivacyCheckInterval)
{
RecheckPrivacy(&Neighbourhood, &CollationBuffers, &BespokeTemplate);
}
sleep(GLOBAL_UPDATE_INTERVAL);
}
sleep(GLOBAL_UPDATE_INTERVAL);
}
}
else
{
fprintf(stderr, "Error: Quitting because inotify initialisation failed with message\n"
" %s\n", strerror(inotifyError));
}
else
{
fprintf(stderr, "Error: Quitting because inotify initialisation failed with message\n"
" %s\n", strerror(inotifyError));
}
DiscardAllAndFreeConfig();
RemoveAndFreeWatchHandles(&WatchHandles);
MEM_TEST_END("main()");
DiscardAllAndFreeConfig();
RemoveAndFreeWatchHandles(&WatchHandles);
MEM_TEST_END("main()");
}
}
Exit();
}