/* _unix_hot_gain(): gain ship. */ static void _unix_hot_gain(u2_noun who, u2_bean mek) { u2_noun hox = u2_dc("scot", 'p', who); c3_c* hox_c = u2_cr_string(hox); c3_c* pax_c = _unix_down(u2_Host.ops_u.hom_c, hox_c + 1); DIR* rid_u = opendir(pax_c); if ( !rid_u ) { if ( u2_yes == mek ) { _unix_mkdir(pax_c); } else return; } else closedir(rid_u); // uL(fprintf(uH, "GAIN %s\n", pax_c)); free(hox_c); u2z(hox); u2_unix_acquire(pax_c); { u2_uhot* hot_u = malloc(sizeof(u2_uhot)); _unix_dir_watch(&hot_u->dir_u, 0, pax_c); u2_cr_mp(hot_u->who_mp, who); hot_u->nex_u = u2_Host.unx_u.hot_u; u2_Host.unx_u.hot_u = hot_u; } }
/* u3_unix_release(): release a lockfile. */ static void u3_unix_release(c3_c* pax_c) { c3_c* paf_c = _unix_down(pax_c, ".vere.lock"); unlink(paf_c); free(paf_c); }
/* _unix_initial_update_dir(): read directory, but don't watch */ static u3_noun _unix_initial_update_dir(c3_c* pax_c) { u3_noun can = u3_nul; DIR* rid_u = opendir(pax_c); if ( !rid_u ) { uL(fprintf(uH, "error opening initial directory: %s: %s\r\n", pax_c, strerror(errno))); return u3_nul; } while ( 1 ) { struct dirent ent_u; struct dirent* out_u; c3_w err_w; if ( 0 != (err_w = readdir_r(rid_u, &ent_u, &out_u)) ) { uL(fprintf(uH, "error loading initial directory %s: %s\r\n", pax_c, strerror(errno))); c3_assert(0); } else if ( !out_u ) { break; } else if ( '.' == out_u->d_name[0] ) { continue; } else { c3_c* pox_c = _unix_down(pax_c, out_u->d_name); struct stat buf_u; if ( 0 != stat(pox_c, &buf_u) ) { uL(fprintf(uH, "initial can't stat %s: %s\r\n", pox_c, strerror(errno))); free(pox_c); continue; } else { if ( S_ISDIR(buf_u.st_mode) ) { can = u3kb_weld(_unix_initial_update_dir(pox_c), can); } else { can = u3kb_weld(_unix_initial_update_file(pox_c), can); } free(pox_c); } } } if ( closedir(rid_u) < 0 ) { uL(fprintf(uH, "error closing initial directory %s: %s\r\n", pax_c, strerror(errno))); } return can; }
/* u2_unix_acquire(): acquire a lockfile, killing anything that holds it. */ void u2_unix_acquire(c3_c* pax_c) { c3_c* paf_c = _unix_down(pax_c, ".vere.lock"); c3_w pid_w; FILE* loq_u; if ( NULL != (loq_u = fopen(paf_c, "r")) ) { if ( 1 != fscanf(loq_u, "%u", &pid_w) ) { uL(fprintf(uH, "lockfile %s is corrupt!\n", paf_c)); kill(getpid(), SIGTERM); sleep(1); c3_assert(0); } else { c3_w i_w; if ( -1 != kill(pid_w, SIGTERM) ) { uL(fprintf(uH, "unix: stopping process %d, live in %s...\n", pid_w, pax_c)); for ( i_w = 0; i_w < 16; i_w++ ) { sleep(1); if ( -1 == kill(pid_w, SIGTERM) ) { break; } } if ( 16 == i_w ) { for ( i_w = 0; i_w < 16; i_w++ ) { if ( -1 == kill(pid_w, SIGKILL) ) { break; } sleep(1); } } if ( 16 == i_w ) { uL(fprintf(uH, "process %d seems unkillable!\n", pid_w)); c3_assert(0); } } uL(fprintf(uH, "unix: stopped old process %u\n", pid_w)); } fclose(loq_u); unlink(paf_c); } loq_u = fopen(paf_c, "w"); fprintf(loq_u, "%u\n", getpid()); { c3_i fid_i = fileno(loq_u); #if defined(U2_OS_linux) fdatasync(fid_i); #else fcntl(fid_i, F_FULLFSYNC); #endif } fclose(loq_u); }
void u3_unix_ef_initial_into() { c3_c* pax_c = _unix_down(U3_LIB, "arvo"); u3_noun can = _unix_initial_update_dir(pax_c); free(pax_c); u3v_plan(u3nq(u3_blip, c3__sync, u3k(u3A->sen), u3_nul), u3nq(c3__into, u3_nul, c3y, can)); }
/* _unix_update_dir(): update directory, producing list of changes * * when changing this, consider whether to also change * _unix_initial_update_dir() */ static u3_noun _unix_update_dir(u3_udir* dir_u) { u3_noun can = u3_nul; c3_assert( c3y == dir_u->dir ); if ( c3y == dir_u->dry ) { return u3_nul; } dir_u->dry = c3y; // Check that old nodes are still there u3_unod* nod_u = dir_u->kid_u; if ( nod_u ) { while ( nod_u ) { if ( c3y == nod_u->dry ) { nod_u = nod_u->nex_u; } else { if ( c3y == nod_u->dir ) { DIR* red_u = opendir(nod_u->pax_c); if ( 0 == red_u ) { u3_unod* nex_u = nod_u->nex_u; can = u3kb_weld(_unix_free_node(nod_u), can); nod_u = nex_u; } else { closedir(red_u); nod_u = nod_u->nex_u; } } else { struct stat buf_u; c3_i fid_i = open(nod_u->pax_c, O_RDONLY, 0644); if ( (fid_i < 0) || (fstat(fid_i, &buf_u) < 0) ) { if ( ENOENT != errno ) { uL(fprintf(uH, "_unix_update_dir: error opening file %s: %s\r\n", nod_u->pax_c, strerror(errno))); } u3_unod* nex_u = nod_u->nex_u; can = u3kb_weld(_unix_free_node(nod_u), can); nod_u = nex_u; } else { if ( close(fid_i) < 0 ) { uL(fprintf(uH, "_unix_update_dir: error closing file %s: %s\r\n", nod_u->pax_c, strerror(errno))); } nod_u = nod_u->nex_u; } } } } } // Check for new nodes DIR* rid_u = opendir(dir_u->pax_c); if ( !rid_u ) { uL(fprintf(uH, "error opening directory %s: %s\r\n", dir_u->pax_c, strerror(errno))); c3_assert(0); } while ( 1 ) { struct dirent ent_u; struct dirent* out_u; c3_w err_w; if ( (err_w = readdir_r(rid_u, &ent_u, &out_u)) != 0 ) { uL(fprintf(uH, "error loading directory %s: %s\r\n", dir_u->pax_c, strerror(err_w))); c3_assert(0); } else if ( !out_u ) { break; } else if ( '.' == out_u->d_name[0] ) { continue; } else { c3_c* pax_c = _unix_down(dir_u->pax_c, out_u->d_name); struct stat buf_u; if ( 0 != stat(pax_c, &buf_u) ) { uL(fprintf(uH, "can't stat %s: %s\r\n", pax_c, strerror(errno))); free(pax_c); continue; } else { u3_unod* nod_u; for ( nod_u = dir_u->kid_u; nod_u; nod_u = nod_u->nex_u ) { if ( 0 == strcmp(pax_c, nod_u->pax_c) ) { if ( S_ISDIR(buf_u.st_mode) ) { if ( c3n == nod_u->dir ) { uL(fprintf(uH, "not a directory: %s\r\n", nod_u->pax_c)); c3_assert(0); } } else { if ( c3y == nod_u->dir ) { uL(fprintf(uH, "not a file: %s\r\n", nod_u->pax_c)); c3_assert(0); } } break; } } if ( !nod_u ) { if ( !S_ISDIR(buf_u.st_mode) ) { if ( !strchr(out_u->d_name,'.') || '~' == out_u->d_name[strlen(out_u->d_name) - 1] || ('#' == out_u->d_name[0] && '#' == out_u->d_name[strlen(out_u->d_name) - 1]) ) { free(pax_c); continue; } u3_ufil* fil_u = c3_malloc(sizeof(u3_ufil)); _unix_watch_file(fil_u, dir_u, pax_c); } else { u3_udir* dis_u = c3_malloc(sizeof(u3_udir)); _unix_watch_dir(dis_u, dir_u, pax_c); can = u3kb_weld(_unix_update_dir(dis_u), can); // XXX unnecessary? } } } } } if ( closedir(rid_u) < 0 ) { uL(fprintf(uH, "error closing directory %s: %s\r\n", dir_u->pax_c, strerror(errno))); } if ( !dir_u->kid_u ) { return u3kb_weld(_unix_free_node((u3_unod*) dir_u), can); } // get change list for ( nod_u = dir_u->kid_u; nod_u; nod_u = nod_u->nex_u ) { can = u3kb_weld(_unix_update_node(nod_u), can); } return can; }
/* _unix_scan_mount_point(): scan unix for already-existing mount point */ static void _unix_scan_mount_point(u3_umon* mon_u) { DIR* rid_u = opendir(mon_u->dir_u.pax_c); if ( !rid_u ) { uL(fprintf(uH, "error opening pier directory: %s: %s\r\n", mon_u->dir_u.pax_c, strerror(errno))); return; } c3_w len_w = strlen(mon_u->nam_c); while ( 1 ) { struct dirent ent_u; struct dirent* out_u; c3_w err_w; if ( 0 != (err_w = readdir_r(rid_u, &ent_u, &out_u)) ) { uL(fprintf(uH, "erroring loading pier directory %s: %s\r\n", mon_u->dir_u.pax_c, strerror(errno))); c3_assert(0); } else if ( !out_u ) { break; } else if ( '.' == out_u->d_name[0] ) { // unnecessary, but consistency continue; } else if ( 0 != strncmp(mon_u->nam_c, out_u->d_name, len_w) ) { continue; } else { c3_c* pax_c = _unix_down(mon_u->dir_u.pax_c, out_u->d_name); struct stat buf_u; if ( 0 != stat(pax_c, &buf_u) ) { uL(fprintf(uH, "can't stat pier directory %s: %s\r\n", mon_u->dir_u.pax_c, strerror(errno))); free(pax_c); continue; } if ( S_ISDIR(buf_u.st_mode) ) { if ( out_u->d_name[len_w] != '\0' ) { free(pax_c); continue; } else { u3_udir* dir_u = c3_malloc(sizeof(u3_udir)); _unix_watch_dir(dir_u, &mon_u->dir_u, pax_c); } } else { if ( '.' != out_u->d_name[len_w] || '\0' == out_u->d_name[len_w + 1] || '~' == out_u->d_name[strlen(out_u->d_name) - 1] || ('#' == out_u->d_name[0] && '#' == out_u->d_name[strlen(out_u->d_name) - 1]) ) { free(pax_c); continue; } else { u3_ufil* fil_u = c3_malloc(sizeof(u3_ufil)); _unix_watch_file(fil_u, &mon_u->dir_u, pax_c); } } } } }
/* _unix_dir_update(): update directory, true if changed. */ static u2_bean _unix_dir_update(u2_udir* dir_u, DIR* rid_u) { u2_bean cha = u2_no; // uL(fprintf(uH, "dir_update ON %s\n", dir_u->pax_c)); /* use dry bits as markers */ { u2_udir* dis_u; u2_ufil* fil_u; for ( dis_u = dir_u->dis_u; dis_u; dis_u = dis_u->nex_u ) { dis_u->dry = u2_yes; } for ( fil_u = dir_u->fil_u; fil_u; fil_u = fil_u->nex_u ) { fil_u->dry = u2_yes; } } /* iterate through directory, opening and updating */ while ( 1 ) { struct dirent ent_u; struct dirent* out_u; if ( readdir_r(rid_u, &ent_u, &out_u) != 0 ) { uL(fprintf(uH, "%s: %s\n", dir_u->pax_c, strerror(errno))); c3_assert(0); } else if ( !out_u ) { break; } else if ( ('.' == out_u->d_name[0]) ) { // XX screws up some paths continue; } else { c3_c* pax_c = _unix_down(dir_u->pax_c, out_u->d_name); struct stat buf_u; // uL(fprintf(uH, " in %s\n", pax_c)); if ( 0 != stat(pax_c, &buf_u) ) { free(pax_c); continue; } else { if ( !S_ISDIR(buf_u.st_mode) ) { mpz_t mod_mp; u2_ufil* fil_u; { u2_noun mod = c3_stat_mtime(&buf_u); u2_cr_mp(mod_mp, mod); u2z(mod); } for ( fil_u = dir_u->fil_u; fil_u; fil_u = fil_u->nex_u ) { if ( !strcmp(pax_c, fil_u->pax_c) ) { fil_u->dry = u2_no; cha = u2_or(cha, _unix_file_update(fil_u, mod_mp)); break; } } if ( !fil_u ) { fil_u = malloc(sizeof(u2_ufil)); // uL(fprintf(uH, "found file %s\n", pax_c)); _unix_file_watch(fil_u, dir_u, pax_c, mod_mp); fil_u->nex_u = dir_u->fil_u; dir_u->fil_u = fil_u; cha = u2_yes; } mpz_clear(mod_mp); } else { DIR* red_u = _unix_opendir(pax_c); u2_udir* dis_u; for ( dis_u = dir_u->dis_u; dis_u; dis_u = dis_u->nex_u ) { if ( !strcmp(pax_c, dis_u->pax_c) ) { dis_u->dry = u2_no; cha = u2_or(cha, _unix_dir_update(dis_u, red_u)); break; } } if ( !dis_u ) { dis_u = malloc(sizeof(u2_udir)); // uL(fprintf(uH, "found directory %s\n", pax_c)); _unix_dir_watch(dis_u, dir_u, pax_c); _unix_dir_update(dis_u, red_u); dis_u->nex_u = dir_u->dis_u; dir_u->dis_u = dis_u; cha = u2_yes; } } } } } /* use dry bits as markers */ { u2_udir** dis_u; u2_ufil** fil_u; for ( dis_u = &(dir_u->dis_u); *dis_u; ) { if ( u2_no == (*dis_u)->dry ) { (*dis_u)->dry = u2_yes; dis_u = &(*dis_u)->nex_u; } else { u2_udir* ded_u = *dis_u; u2_udir* nex_u = ded_u->nex_u; // uL(fprintf(uH, "removed directory %s\n", ded_u->pax_c)); _unix_dir_free(ded_u); free(ded_u); *dis_u = nex_u; cha = u2_yes; } } for ( fil_u = &(dir_u->fil_u); *fil_u; ) { if ( u2_no == (*fil_u)->dry ) { fil_u = &(*fil_u)->nex_u; } else { u2_ufil* ded_u = *fil_u; u2_ufil* nex_u = ded_u->nex_u; // uL(fprintf(uH, "removed file %s\n", ded_u->pax_c)); _unix_file_free(ded_u); free(ded_u); *fil_u = nex_u; cha = u2_yes; } } } closedir(rid_u); // uL(fprintf(uH, "dir_update OFF %s\n", dir_u->pax_c)); return cha; }
/* _unix_dir_update(): update directory. */ static void _unix_dir_update(u2_udir* dir_u, DIR* rid_u) { if ( u2_yes == dir_u->dry ) { return; } else { // Update all wet subdirectories. // u2_udir** dis_u; u2_ufil** fil_u; for ( dis_u = &(dir_u->dis_u); *dis_u; ) { if ( u2_yes == (*dis_u)->dry ) { dis_u = &(*dis_u)->nex_u; } else { DIR* red_u = opendir((*dis_u)->pax_c); if ( 0 == red_u ) { u2_udir* ded_u = *dis_u; u2_udir* nex_u = ded_u->nex_u; // uL(fprintf(uH, "removed directory %s\n", ded_u->pax_c)); _unix_dir_free(ded_u); *dis_u = nex_u; } else { _unix_dir_update(*dis_u, red_u); closedir(red_u); dis_u = &(*dis_u)->nex_u; } } } // Check all wet files to see if they need deleting. // for ( fil_u = &(dir_u->fil_u); *fil_u; ) { if ( u2_yes == (*fil_u)->dry ) { fil_u = &(*fil_u)->nex_u; } else { struct stat buf_u; if ( -1 == stat((*fil_u)->pax_c, &buf_u) || !(S_IFREG & buf_u.st_mode) ) { u2_ufil* ded_u = *fil_u; u2_ufil* nex_u = ded_u->nex_u; // uL(fprintf(uH, "removed file %s\n", ded_u->pax_c)); _unix_file_free(ded_u); *fil_u = nex_u; } else { fil_u = &(*fil_u)->nex_u; } } } // Scan for new files/directories. XX - this is O(n^2) brute // force, and could be done by smarter event processing. // while ( 1 ) { struct dirent ent_u; struct dirent* out_u; if ( readdir_r(rid_u, &ent_u, &out_u) != 0 ) { // uL(fprintf(uH, "%s: %s\n", dir_u->pax_c, strerror(errno))); c3_assert(0); } else if ( !out_u ) { break; } else if ( ('.' == out_u->d_name[0]) ) { // XX screws up some paths continue; } else { c3_c* pax_c = _unix_down(dir_u->pax_c, out_u->d_name); struct stat buf_u; // uL(fprintf(uH, " in %s\n", pax_c)); if ( 0 != stat(pax_c, &buf_u) ) { free(pax_c); continue; } else { if ( !S_ISDIR(buf_u.st_mode) ) { mpz_t mod_mp; u2_ufil* fil_u; if ( ( NULL == strrchr(out_u->d_name, '.')) || ( '~' == out_u->d_name[strlen(out_u->d_name) - 1] ) ) { continue; } { u2_noun mod = c3_stat_mtime(&buf_u); u2_cr_mp(mod_mp, mod); u2z(mod); } for ( fil_u = dir_u->fil_u; fil_u; fil_u = fil_u->nex_u ) { if ( !strcmp(pax_c, fil_u->pax_c) ) { break; } } if ( !fil_u ) { fil_u = c3_malloc(sizeof(u2_ufil)); // uL(fprintf(uH, "found file %s\n", pax_c)); _unix_file_watch(fil_u, dir_u, pax_c, mod_mp); fil_u->nex_u = dir_u->fil_u; dir_u->fil_u = fil_u; } mpz_clear(mod_mp); } else { u2_udir* dis_u; for ( dis_u = dir_u->dis_u; dis_u; dis_u = dis_u->nex_u ) { if ( !strcmp(pax_c, dis_u->pax_c) ) { break; } } if ( !dis_u ) { DIR* red_u = _unix_opendir(pax_c); dis_u = c3_malloc(sizeof(u2_udir)); // uL(fprintf(uH, "found directory %s\n", pax_c)); _unix_dir_watch(dis_u, dir_u, pax_c); _unix_dir_update(dis_u, red_u); dis_u->nex_u = dir_u->dis_u; dir_u->dis_u = dis_u; closedir(red_u); } else { free(pax_c); } } } } } } }