diff options
Diffstat (limited to 'src/main/c/paisa-fleet/FindFullDisks.c')
-rw-r--r-- | src/main/c/paisa-fleet/FindFullDisks.c | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/src/main/c/paisa-fleet/FindFullDisks.c b/src/main/c/paisa-fleet/FindFullDisks.c new file mode 100644 index 0000000..429b71c --- /dev/null +++ b/src/main/c/paisa-fleet/FindFullDisks.c @@ -0,0 +1,383 @@ +#if 0 + +true `# configure FindFullDisks for NORMAL systems` \ + && CC=gcc \ + && MKDIR_P="mkdir -p" \ + && CFLAGS="-Wall -Werror -pedantic -Os -fmax-errors=1 -Wno-error=unused-variable -Wno-error=unused-function -Isrc/main/c -Iimport/include" \ + && LDFLAGS="-Wl,-dn,-lgarbage,-lcJSON,-lexpat,-lmbedtls,-lmbedx509,-lmbedcrypto,-dy,-lpthread,-lws2_w32,-Limport/lib" \ + && true + +true `# configure FindFullDisks for BROKEN systems` \ + && CC=x86_64-w64-mingw32-gcc \ + && MKDIR_P="mkdir -p" \ + && CFLAGS="-Wall -Werror -pedantic -Os -fmax-errors=1 -Wno-error=unused-variable -Wno-error=unused-function -Isrc/main/c -Iimport/include" \ + && LDFLAGS="-Wl,-dn,-lgarbage,-lcJSON,-lexpat,-lmbedtls,-lmbedx509,-lmbedcrypto,-dy,-lws2_32,-Limport/lib" \ + && true + +true `# make FindFullDisks` \ + && ${MKDIR_P:?} build/bin \ + && ${CC:?} -o build/bin/findfulldisks $CFLAGS src/main/c/paisa-fleet/FindFullDisks.c $LDFLAGS \ + && true + +#endif + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "Garbage.h" + +#define FLG_isHelp (1<<0) + +#if !NDEBUG +# define REGISTER register +# define LOGDBG(...) fprintf(stderr, __VA_ARGS__) +# define IF_DBG(expr) expr +#else +# define REGISTER +# define LOGDBG(...) +# define IF_DBG(expr) +#endif +#define LOGERR(...) fprintf(stderr, __VA_ARGS__) + + + +typedef struct FindFullDisks FindFullDisks; +typedef struct Device Device; + + +#define MAGIC_FindFullDisks 0xB5410200 +struct FindFullDisks { + IF_DBG(int mAGIC); + int flg; + const char *sshUser; + int sshPort; + int maxParallel, numInProgress; + struct GarbageEnv **env; + struct Garbage_CsvIStream **csvSrc; + struct Garbage_Process **child; + char *inBuf; + int inBuf_cap, inBuf_len; + Device *devices; + int devices_cap, devices_cnt; + int iDevice; /* Next device to be triggered. */ + int exitCode; +}; + + +#define MAGIC_Device 0xAB420200 +struct Device { + IF_DBG(int mAGIC); + struct FindFullDisks *app; + char hostname[sizeof"lunkwill-0123456789AB_____"]; + char eddieName[sizeof"eddie12345_____"]; + char stdoutBuf[8192]; + int stdoutBuf_cap, stdoutBuf_len; +}; + + +/*BEG fwd decls*/ +static void beginNextDevice( void* ); +static void feedNextChunkFromStdinToCsvParser( void* ); +/*END fwd decls*/ + + +static void printHelp( void ){ + printf("%s%s%s", " \n" + " ", strrchr(__FILE__,'/')+1, "\n" + " \n" + " Expected format on stdin is a CSV like:\n" + " \n" + " eddie00042 <SEMICOLON> lunkwill-ABBABEAFABBA <LF>\n" + " ...\n" + " \n" + " Options:\n" + " \n" + " --sshUser <str>\n" + " \n" + " --sshPort <int>\n" + " Default: 22\n" + " \n" + " --maxParallel <int>\n" + " Default 1.\n" + " \n"); +} + + +static int parseArgs( int argc, char**argv, FindFullDisks*app ){ + int iA = 1; + app->sshUser = NULL; + app->sshPort = 22; + app->maxParallel = 1; +nextArg:; + const char *arg = argv[iA++]; + if( arg == NULL ) goto validateArgs; + if( !strcmp(arg, "--help")){ + app->flg |= FLG_isHelp; return 0; + }else if( !strcmp(arg, "--sshUser")){ + arg = argv[iA++]; + if( arg == NULL ){ LOGERR("EINVAL: Arg --sshUser needs value\n"); return -1; } + app->sshUser = arg; + }else if( !strcmp(arg, "--sshPort")){ + arg = argv[iA++]; + if( arg == NULL ){ LOGERR("EINVAL: Arg --sshPort needs value\n"); return -1; } + errno = 0; + app->sshPort = strtol(arg, NULL, 0); + if( errno ){ LOGERR("EINVAL: --sshPort %s\n", arg); return -1; } + }else if( !strcmp(arg, "--maxParallel")){ + arg = argv[iA++]; + if( arg == NULL ){ LOGERR("EINVAL: Arg --maxParallel needs value\n"); return -1; } + errno = 0; + app->maxParallel = strtol(arg, NULL, 0); + if( errno ){ LOGERR("EINVAL: --maxParallel %s\n", arg); return -1; } + }else{ + LOGERR("EINVAL: %s\n", arg); + } + goto nextArg; +validateArgs:; + if( app->sshUser == NULL ){ LOGERR("EINVAL: Arg --sshUser missing\n"); return -1; } + return 0; +} + + +static void no_op( void*_ ){} + + +static void examineDeviceResult( void*device_ ){ + REGISTER int err; + Device*const device = device_; assert(device->mAGIC = MAGIC_Device); + //FindFullDisks*const app = device->app; assert(app->mAGIC == MAGIC_FindFullDisks); + FILE *outFd = NULL; + if( device->stdoutBuf_len <= 0 ){ /*nothing to do*/ goto endFn; } + char outName[sizeof"result/eddie12345-lunkwill-1234567890123456.log"]; + err = snprintf(outName, sizeof outName, "result/%s-%s.log", device->eddieName, device->hostname); + assert(err < sizeof outName); + outFd = fopen(outName, "wb"); + if( outFd == NULL ){ LOGDBG("assert(fopen(%s) != %d) %s:%d\n", outName, errno, __FILE__, __LINE__); abort(); } + err = fwrite(device->stdoutBuf, 1, device->stdoutBuf_len, outFd); + assert(err == device->stdoutBuf_len); +endFn: + if( outFd != NULL ) fclose(outFd); +} + + +static void Child_onStdout( const char*buf, int buf_len, void*cls ){ + Device*const device = cls; assert(device->mAGIC = MAGIC_Device); + FindFullDisks*const app = device->app; assert(app->mAGIC == MAGIC_FindFullDisks); + if( buf_len > 0 ){ /*another chunk*/ + if( device->stdoutBuf_len + buf_len >= device->stdoutBuf_cap ) assert(!"TODO_VD8CAIVAgBDwIA4mECAKVjAgB1XwIAfk"); + memcpy(device->stdoutBuf + device->stdoutBuf_len, buf, buf_len); + device->stdoutBuf_len += buf_len; + //printf("%.*s", buf_len, buf); + }else{ /*EOF*/ + assert(buf_len == 0); + } +} + + +static void Child_onJoined( int retval, int exitCode, int sigNum, void*cls ){ + Device*const device = cls; assert(device->mAGIC == MAGIC_Device); + FindFullDisks*const app = device->app; assert(app->mAGIC == MAGIC_FindFullDisks); + if( retval != 0 || exitCode != 0 || sigNum != 0 ){ + LOGDBG("[DEBUG] %s(%d, %d, %d)\n", __func__, retval, exitCode, sigNum); + } + assert(app->numInProgress > 0); + app->numInProgress -= 1; + (*app->env)->enqueBlocking(app->env, examineDeviceResult, device); + (*app->env)->enqueBlocking(app->env, beginNextDevice, app); +} + + +static void visitDevice( FindFullDisks*app, Device*device ){ + assert(device != NULL && device->mAGIC == MAGIC_Device); + assert(device < app->devices + app->devices_cnt); + LOGERR("\n[INFO ] %s \"%s\" (behind \"%s\")\n", __func__, device->hostname, device->eddieName); + int err; + char eddieCmd[2048]; + //err = snprintf(eddieCmd, sizeof eddieCmd, "true" + // " && HOSTNAME=$(hostname|sed 's_.pnet.ch__')" + // " && STAGE=$PAISA_ENV" + // " && printf \"remoteEddieName=$HOSTNAME, remoteStage=$STAGE\\n\"" + // " && if test \"$(echo ${HOSTNAME}|sed -E 's_^vted_teddie_g')\" != \"%s\"; then true" + // " && echo wrong host. Want %s found $HOSTNAME && false" + // " ;fi" + // " && df", + // device->eddieName, device->eddieName + //); + err = snprintf(eddieCmd, sizeof eddieCmd, "true" + " && HOSTNAME=$(hostname|sed 's_.pnet.ch__')" + " && STAGE=$PAISA_ENV" + " && printf \"remoteEddieName=$HOSTNAME, remoteStage=$STAGE\\n\"" + " && if test \"$(echo ${HOSTNAME}|sed -E 's_^vted_teddie_g')\" != \"%s\"; then true" + " && echo wrong host. Want %s found $HOSTNAME && false" + " ;fi" + " && ssh -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null" + " -p%d %s@%s" + " -- sh -c 'true" + " && HOSTNAME=$(hostname|sed '\"'\"'s_.isa.localdomain__'\"'\"')" + " && STAGE=$PAISA_ENV" + " && printf \"remoteHostname=$HOSTNAME, remoteStage=$STAGE\\n\"" + // on some machine, df failed with "Stale file handle" But I want to + // continue with next device regardless of such errors. + " && df || true" + "'", + device->eddieName, device->eddieName, app->sshPort, app->sshUser, + strncmp("fook-",device->hostname,5) ? device->hostname : "fook" + ); + assert(err < sizeof eddieCmd); + assert(app->sshPort > 0 && app->sshPort <= 0xFFFF); + char sshPortStr[sizeof"65535"]; + err = snprintf(sshPortStr, sizeof sshPortStr, "%d", app->sshPort); + assert(err < (int)sizeof sshPortStr); + char userAtEddie[64]; + err = snprintf(userAtEddie, sizeof userAtEddie, "%s@%s", app->sshUser, device->eddieName); + assert(err < sizeof userAtEddie); + char *childArgv[] = { "ssh", + "-oRemoteCommand=none", + "-oStrictHostKeyChecking=no", + "-oUserKnownHostsFile=/dev/null", + "-oConnectTimeout=4", + "-p", sshPortStr, + userAtEddie, + "--", "sh", "-c", eddieCmd, + NULL + }; + //LOGDBG("CMDLINE:"); + //for( int i = 0 ; childArgv[i] != NULL ; ++i ) LOGDBG(" \"%s\"", childArgv[i]); + //LOGDBG("\n\n"); + app->child = (*app->env)->newProcess(app->env, &(struct Garbage_Process_Mentor){ + .cls = device, + .usePathSearch = !0, + .argv = childArgv, + .onStdout = Child_onStdout, + //.onStderr = , + .onJoined = Child_onJoined, + }); + assert(app->child != NULL); + (*app->child)->join(app->child, 42000); +} + + +static void beginNextDevice( void*cls ){ + FindFullDisks*const app = cls; assert(app->mAGIC == MAGIC_FindFullDisks); +maybeBeginAnotherOne: + if( app->numInProgress >= app->maxParallel ){ + //LOGDBG("[DEBUG] Already %d/%d in progress. Do NOT trigger more for now.\n", + // app->numInProgress, app->maxParallel); + goto endFn; + } + if( app->iDevice >= app->devices_cnt ){ + //LOGDBG("[INFO ] Work on %d devices triggered. No more devices to trigger.\n", app->iDevice); + goto endFn; + } + assert(app->iDevice >= 0 && app->iDevice < INT_MAX); + app->iDevice += 1; + assert(app->numInProgress >= 0 && app->numInProgress < INT_MAX); + app->numInProgress += 1; + visitDevice(app, app->devices + app->iDevice - 1); + goto maybeBeginAnotherOne; +endFn:; +} + + +static void onCsvRow( struct Garbage_CsvIStream_BufWithLength*row, int numCols, void*cls ){ + REGISTER int err; + FindFullDisks*const app = cls; assert(app->mAGIC == MAGIC_FindFullDisks); + if( app->exitCode ) return; + if( numCols != 2 ){ + LOGERR("[ERROR] Expected 2 column in input CSV but found %d\n", numCols); + app->exitCode = -1; return; + } + if( app->devices_cap <= app->devices_cnt ){ + app->devices_cap += 4096; + void *tmp = realloc(app->devices, app->devices_cap*sizeof*app->devices); + if( tmp == NULL ) assert(!"TODO_c04CAJtRAgDYWQIAm10CAOAeAgA0KgIA"); + app->devices = tmp; + } + #define DEVICE (app->devices + app->devices_cnt) + IF_DBG(DEVICE->mAGIC = MAGIC_Device); + DEVICE->app = app; + DEVICE->stdoutBuf_cap = sizeof DEVICE->stdoutBuf / sizeof*DEVICE->stdoutBuf; + if( row[0].len >= sizeof DEVICE->eddieName ){ + LOGERR("[ERROR] eddieName too long: len=%d\n", row[0].len); + app->exitCode = -1; return; + } + if( row[1].len >= sizeof DEVICE->hostname ){ + LOGERR("[ERROR] hostname too long: len=%d\n", row[1].len); + app->exitCode = -1; return; + } + memcpy(DEVICE->eddieName, row[0].buf, row[0].len); + DEVICE->eddieName[row[0].len] = '\0'; + memcpy(DEVICE->hostname, row[1].buf, row[1].len); + DEVICE->hostname[row[1].len] = '\0'; + #undef DEVICE + app->devices_cnt += 1; +} + + +static void onCsvParserCloseSnkDone( int retval, void*app_ ){ + FindFullDisks*const app = app_; assert(app->mAGIC == MAGIC_FindFullDisks); + LOGDBG("[DEBUG] Found %d devices in input.\n", app->devices_cnt); + (*app->env)->enqueBlocking(app->env, beginNextDevice, app); +} + + +static void onCsvParserWriteDone( int retval, void*cls ){ + FindFullDisks*const app = cls; assert(app->mAGIC == MAGIC_FindFullDisks); + if( retval <= 0 ){ LOGDBG("assert(retval != %d) %s:%d\n", retval, __FILE__, __LINE__); abort(); } + (*app->env)->enqueBlocking(app->env, feedNextChunkFromStdinToCsvParser, app); +} + + +static void feedNextChunkFromStdinToCsvParser( void*cls ){ + REGISTER int err; + FindFullDisks*const app = cls; assert(app->mAGIC == MAGIC_FindFullDisks); + if( app->exitCode ) return; + #define SRC (stdin) + if( app->inBuf == NULL || app->inBuf_cap < 1<<15 ){ + app->inBuf_cap = 1<<15; + if( app->inBuf ) free(app->inBuf); + app->inBuf = malloc(app->inBuf_cap*sizeof*app->inBuf);; + if( app->inBuf == NULL ){ assert(!"TODO_TT8CAGQLAgCoawIA9jgCANA6AgBTaAIA"); } + } + err = fread(app->inBuf, 1, app->inBuf_cap, SRC); + if( err <= 0 ){ + (*app->csvSrc)->closeSnk(app->csvSrc, onCsvParserCloseSnkDone, app); + return; + } + app->inBuf_len = err; + (*app->csvSrc)->write(app->inBuf, app->inBuf_len, app->csvSrc, onCsvParserWriteDone, app); + #undef SRC +} + + +static void initCsvParserForDeviceListOnStdin( void*cls ){ + FindFullDisks*const app = cls; assert(app->mAGIC == MAGIC_FindFullDisks); + static struct Garbage_CsvIStream_Mentor csvMentor = { + .onCsvRow = onCsvRow, + .onCsvDocEnd = no_op, + }; + struct Garbage_CsvIStream_Opts csvOpts = { .delimCol = ';' }; + app->csvSrc = (*app->env)->newCsvIStream(app->env, &csvOpts, &csvMentor, app); + feedNextChunkFromStdinToCsvParser(app); +} + + +int main( int argc, char**argv ){ + void *envMemory[SIZEOF_struct_GarbageEnv/sizeof(void*)]; + FindFullDisks app = {0}; assert((void*)0 == NULL); + #define app (&app) + IF_DBG(app->mAGIC = MAGIC_FindFullDisks); + if( parseArgs(argc, argv, app) ){ app->exitCode = -1; goto endFn; } + if( app->flg & FLG_isHelp ){ printHelp(); goto endFn; } + app->env = GarbageEnv_ctor(envMemory, sizeof envMemory); + assert(app->env != NULL); + (*app->env)->enqueBlocking(app->env, initCsvParserForDeviceListOnStdin, app); + (*app->env)->runUntilDone(app->env); +endFn: + return !!app->exitCode; + #undef app +} + + |