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 = { version CINERA_APP_VERSION = {
.Major = 0, .Major = 0,
.Minor = 7, .Minor = 7,
.Patch = 17 .Patch = 18
}; };
#include <stdarg.h> // NOTE(matt): varargs #include <stdarg.h> // NOTE(matt): varargs
@ -4735,7 +4735,6 @@ void *
LocateBlock(block_id BlockID) LocateBlock(block_id BlockID)
{ {
void *Result = 0; void *Result = 0;
db_header *Header = (db_header *)DB.Metadata.File.Buffer.Location; db_header *Header = (db_header *)DB.Metadata.File.Buffer.Location;
char *Ptr = (char *)Header; char *Ptr = (char *)Header;
Ptr += sizeof(db_header); Ptr += sizeof(db_header);
@ -14133,9 +14132,10 @@ WriteEntireDatabase()
fclose(DB.Metadata.File.Handle); fclose(DB.Metadata.File.Handle);
} }
int rc
InitDB(void) 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 // 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 // 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() // 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)); 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)); fprintf(stderr, "Unable to create directory %.*s: %s\n", (int)Config->DatabaseLocation.Length, Config->DatabaseLocation.Base, strerror(errno));
Free(DatabaseLocation0); Free(DatabaseLocation0);
return RC_ERROR_DIRECTORY; Result = RC_ERROR_DIRECTORY;
}; };
} }
else else
@ -14271,39 +14271,43 @@ InitDB(void)
} }
Free(DatabaseLocation0); Free(DatabaseLocation0);
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w"); if(Result == RC_SUCCESS)
if(DB.Metadata.File.Handle)
{ {
fwrite(&DB.Header, sizeof(DB.Header), 1, DB.Metadata.File.Handle);
DB.Metadata.Signposts.ProjectsBlock.Byte = ftell(DB.Metadata.File.Handle); DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
fwrite(&DB.ProjectsBlock, sizeof(DB.ProjectsBlock), 1, DB.Metadata.File.Handle); 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); DB.Metadata.Signposts.ProjectsBlock.Byte = ftell(DB.Metadata.File.Handle);
fwrite(&DB.AssetsBlock, sizeof(DB.AssetsBlock), 1, DB.Metadata.File.Handle); fwrite(&DB.ProjectsBlock, sizeof(DB.ProjectsBlock), 1, DB.Metadata.File.Handle);
fclose(DB.Metadata.File.Handle); DB.Metadata.Signposts.AssetsBlock.Byte = ftell(DB.Metadata.File.Handle);
ReadFileIntoBuffer(&DB.Metadata.File); fwrite(&DB.AssetsBlock, sizeof(DB.AssetsBlock), 1, DB.Metadata.File.Handle);
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; fclose(DB.Metadata.File.Handle);
} ReadFileIntoBuffer(&DB.Metadata.File);
else 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;
// TODO(matt): Handle unopenable database files }
PrintC(CS_RED, "Could not open database file: "); else
PrintC(CS_MAGENTA_BOLD, DB.Metadata.File.Path); {
fprintf(stderr, "\n"); // TODO(matt): Handle unopenable database files
_exit(0); PrintC(CS_RED, "Could not open database file: ");
} PrintC(CS_MAGENTA_BOLD, DB.Metadata.File.Path);
fprintf(stderr, "\n");
_exit(0);
}
#if 0 #if 0
DB.File.Handle = fopen(DB.File.Path, "w"); DB.File.Handle = fopen(DB.File.Path, "w");
fprintf(DB.File.Handle, "---\n"); fprintf(DB.File.Handle, "---\n");
fclose(DB.File.Handle); fclose(DB.File.Handle);
ReadFileIntoBuffer(&DB.File); ReadFileIntoBuffer(&DB.File);
#endif #endif
}
} }
return RC_SUCCESS; return Result;
} }
void void
@ -15340,58 +15344,63 @@ PrintEvent(struct inotify_event *Event, int EventIndex, int Indentation)
} }
#endif #endif
void rc
InitAll(neighbourhood *Neighbourhood, buffers *CollationBuffers, template *BespokeTemplate) InitAll(neighbourhood *Neighbourhood, buffers *CollationBuffers, template *BespokeTemplate)
{ {
rc Result = RC_SUCCESS;
MEM_TEST_TOP("InitAll()"); MEM_TEST_TOP("InitAll()");
RewindCollationBuffers(CollationBuffers); RewindCollationBuffers(CollationBuffers);
/* */ MEM_TEST_MID("InitAll()"); /* */ MEM_TEST_MID("InitAll()");
/* +MEM */ InitDB(); /* +MEM */ Result = InitDB();
/* */ MEM_TEST_MID("InitAll()"); /* */ MEM_TEST_MID("InitAll()");
// TODO(matt): Straight up remove these PrintAssetsBlock() calls if(Result == RC_SUCCESS)
//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()"); // TODO(matt): Straight up remove these PrintAssetsBlock() calls
/* +MEM */ SyncProject(GetPlaceInBook(&Config->Project, i), Neighbourhood, CollationBuffers, BespokeTemplate, &TitledSync); //PrintAssetsBlock(0);
/* */ MEM_TEST_MID("InitAll()");
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]);
} }
return Result;
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]);
} }
void void
@ -15672,6 +15681,7 @@ void
Exit(void) Exit(void)
{ {
Free(MemoryArena.Location); Free(MemoryArena.Location);
fprintf(stderr, "Exiting\n");
_exit(0); _exit(0);
} }
@ -15812,6 +15822,7 @@ main(int ArgC, char **Args)
PushWatchHandle(ConfigPathL, EXT_NULL, WT_CONFIG, 0, 0); PushWatchHandle(ConfigPathL, EXT_NULL, WT_CONFIG, 0, 0);
Config = ParseConfig(ConfigPathL, &TokensList); Config = ParseConfig(ConfigPathL, &TokensList);
rc Succeeding = RC_SUCCESS;
if(Config) if(Config)
{ {
if(Mode & MODE_EXAMINE) if(Mode & MODE_EXAMINE)
@ -15828,7 +15839,7 @@ main(int ArgC, char **Args)
else else
{ {
/* */ MEM_TEST_MID("main()"); /* */ MEM_TEST_MID("main()");
/* +MEM */ InitAll(&Neighbourhood, &CollationBuffers, &BespokeTemplate); /* +MEM */ Succeeding = InitAll(&Neighbourhood, &CollationBuffers, &BespokeTemplate);
/* */ MEM_TEST_MID("main()"); /* */ 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 while(MonitorFilesystem(&Neighbourhood, &CollationBuffers, &BespokeTemplate, ConfigPathL, &TokensList) != RC_ARENA_FULL)
//
// 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); // 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
else {
{ fprintf(stderr, "Error: Quitting because inotify initialisation failed with message\n"
fprintf(stderr, "Error: Quitting because inotify initialisation failed with message\n" " %s\n", strerror(inotifyError));
" %s\n", strerror(inotifyError)); }
}
DiscardAllAndFreeConfig(); DiscardAllAndFreeConfig();
RemoveAndFreeWatchHandles(&WatchHandles); RemoveAndFreeWatchHandles(&WatchHandles);
MEM_TEST_END("main()"); MEM_TEST_END("main()");
}
} }
Exit(); Exit();
} }