/*
* Copyright (C) 2014 Kevin Martin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/*
* An example repository definition
*
* gitexec = "/usr/bin/git"
*
* gitrepos = {
* luapgsql = "/home/kev82/luapgsql.git"
* }
*
* fossilrepos = {
* luapgsql = "/home/kev82/luapgsql.fossil"
* }
*
* branchtracks = {}
* local function nbt(t) branchtracks[#branchtracks+1] = t end
*
* nbt({
* gitrepos = "luapgsql",
* gitbranch = "master",
* fossilrepos = "luapgsql"})
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <lua.h>
#include <lauxlib.h>
static int checkdir(const char *path) {
int fd = open(path, O_RDONLY);
if(fd == -1) return fd;
struct stat s;
if(fstat(fd, &s) != 0) {
close(fd);
return -1;
}
if(!S_ISDIR(s.st_mode)) {
close(fd);
return -1;
}
return fd;
}
static int checkexec(const char *path) {
int fd = open(path, O_RDONLY);
if(fd == -1) return fd;
struct stat s;
if(fstat(fd, &s) != 0) {
close(fd);
return -1;
}
close(fd);
if(!S_ISREG(s.st_mode)) {
return -1;
}
if((s.st_mode & S_IXOTH) == 0) {
return -1;
}
return 0;
}
#include <sqlite3.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
int gfs_fossilHasTag(const char *fossilPath, const char *tag) {
sqlite3 *db = NULL;
int rc = sqlite3_open_v2(fossilPath, &db,
SQLITE_OPEN_READONLY, NULL);
assert(rc == SQLITE_OK);
assert(db != NULL);
sqlite3_stmt *stmt = NULL;
rc = sqlite3_prepare_v2(db,
"select tagname from tag", -1, &stmt,
NULL);
assert(stmt != NULL);
assert(rc == SQLITE_OK);
rc = sqlite3_step(stmt);
int rv = 0;
while(rc == SQLITE_ROW) {
if(sqlite3_column_type(stmt, 0) == SQLITE_TEXT) {
if(strcmp(sqlite3_column_text(stmt, 0),
tag) == 0) rv = 1;
}
rc = sqlite3_step(stmt);
}
assert(rc == SQLITE_DONE);
sqlite3_finalize(stmt);
sqlite3_close(db);
return rv;
}
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
int gfs_gitFetch(const char *gitExec, int dirfd) {
char *args[] = {
strdup(gitExec),
strdup("fetch"),
NULL};
char *const env[] = {
NULL};
int pid = fork();
if(pid == 0) {
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
open("/dev/null",O_RDONLY);
open("/dev/null",O_WRONLY);
open("/dev/null",O_WRONLY);
fchdir(dirfd);
close(dirfd);
execve(gitExec, args, env);
}
free(args[0]);
free(args[1]);
assert(pid != -1);
int status = 0;
waitpid(pid, &status, 0);
if(!WIFEXITED(status)) return -1;
return WEXITSTATUS(status);
}
int gfs_gitLatestCID(const char *gitExec, int dirfd, const char *branch,
char **cid) {
int pipefd[2];
int status = pipe(pipefd);
assert(status != -1);
char *args[] = {
strdup(gitExec),
strdup("rev-list"),
strdup(branch),
NULL};
char *const env[] = {
NULL};
int pid = fork();
if(pid == 0) {
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
open("/dev/null",O_RDONLY);
dup(pipefd[1]);
open("/dev/null",O_WRONLY);
close(pipefd[0]);
close(pipefd[1]);
fchdir(dirfd);
execve(gitExec, args, env);
}
char **a2 = args;
while(*a2 != NULL) {
free(*a2);
++a2;
}
assert(pid != -1);
close(pipefd[1]);
FILE *stream = fdopen(pipefd[0], "r");
char *lastcid = NULL;
size_t bytes = 0;
status = getline(&lastcid, &bytes, stream);
fclose(stream);
assert(status != -1);
char *lastcid2 = lastcid;
while(*lastcid2 != 0) {
if(*lastcid2 == '\n') *lastcid2 = 0;
++lastcid2;
}
status = 0;
waitpid(pid, &status, 0);
if(WIFSIGNALED(status) && WTERMSIG(status) == SIGPIPE) {
*cid = lastcid;
return 0;
}
if(WIFEXITED(status)) {
*cid = lastcid;
return WEXITSTATUS(status);
}
free(lastcid);
return -1;
}
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <lua.h>
#include <lauxlib.h>
static int getGitExec(lua_State *l) {
lua_settop(l, 0); //[]
lua_getglobal(l, "gitexec"); //[s]
assert(lua_type(l, -1) == LUA_TSTRING);
const char *gitExec = lua_tostring(l, -1);
if(checkexec(gitExec) != 0) {
return luaL_error(l, "Unable to find git '%s'", gitExec);
}
return 1;
}
static int cmd_fetchgit(lua_State *l) {
lua_settop(l, 0); //[]
lua_pushcfunction(l, getGitExec); //[f]
lua_call(l, 0, 1); //[s]
assert(lua_type(l, -1) == LUA_TSTRING);
lua_getglobal(l, "gitrepos"); //[st]
assert(lua_type(l, -1) == LUA_TTABLE);
lua_pushnil(l); //[st_]
while(lua_next(l, -2) != 0) { //[stss] | [st]
int dirfd = checkdir(lua_tostring(l, -1));
int status = gfs_gitFetch(lua_tostring(l, 1), dirfd);
close(dirfd);
if(status != 0) {
return luaL_error(l, "Error gitfetch %s ('%s')",
lua_tostring(l, -1), lua_tostring(l, -2));
}
lua_pop(l, 1); //[sts]
}
//[st]
lua_pushinteger(l, 0); //[stn]
return 1;
}
static int checkTrackedBranch(lua_State *l) {
lua_settop(l, 1); //[t]
luaL_checktype(l, 1, LUA_TTABLE);
char *msgBuf = NULL;
size_t msgBufBytes = 0;
FILE *msgStream = open_memstream(&msgBuf, &msgBufBytes);
assert(msgStream != NULL);
lua_pushcfunction(l, getGitExec); //[tf]
lua_call(l, 0, 1); //[ts]
assert(lua_type(l, -1) == LUA_TSTRING);
lua_getglobal(l, "gitrepos"); //[tst]
assert(lua_type(l, -1) == LUA_TTABLE);
lua_getfield(l, 1, "gitrepos"); //[tsts]
assert(lua_type(l, -1) == LUA_TSTRING);
fprintf(msgStream, "%s", lua_tostring(l, -1));
lua_gettable(l, -2); //[tsts]
assert(lua_type(l, -1) == LUA_TSTRING);
fprintf(msgStream, " (%s)", lua_tostring(l, -1));
int dirfd = checkdir(lua_tostring(l, -1));
lua_pop(l, 2); //[ts]
lua_getfield(l, 1, "gitbranch"); //[[tss]
assert(lua_type(l, -1) == LUA_TSTRING);
fprintf(msgStream, " %s", lua_tostring(l, -1));
char *cid = NULL;
int status = gfs_gitLatestCID(lua_tostring(l, -2),
dirfd, lua_tostring(l, -1), &cid);
close(dirfd);
if(status != 0) {
if(cid != NULL) free(cid);
fprintf(msgStream, ": Failed to get CID");
fclose(msgStream);
lua_pushboolean(l, 0); //[tssb]
lua_pushstring(l, msgBuf);
free(msgBuf);
return 2;
}
assert(cid != NULL);
lua_remove(l, -2); //[ts]
fprintf(msgStream, " %s", cid);
char *tag = NULL;
size_t bytes = 0;
FILE *stream = open_memstream(&tag, &bytes);
fprintf(stream, "sym-gfs-%s-%s", lua_tostring(l, -1), cid);
lua_pop(l, 1); //[t]
fclose(stream);
free(cid);
lua_getglobal(l, "fossilrepos"); //[tt]
assert(lua_type(l, -1) == LUA_TTABLE);
lua_getfield(l, 1, "fossilrepos"); //[tts]
assert(lua_type(l, -1) == LUA_TSTRING);
lua_gettable(l, -2); //[tts]
assert(lua_type(l, -1) == LUA_TSTRING);
lua_remove(l, -2); //[ts]
int hasTag = gfs_fossilHasTag(lua_tostring(l, -1), tag);
free(tag);
lua_pop(l, 1); //[t]
if(!hasTag) {
fprintf(msgStream, ": not found");
lua_pushboolean(l, 0); //[tb]
} else {
fprintf(msgStream, ": found");
lua_pushboolean(l, 1); //[tbb]
}
fclose(msgStream);
lua_pushstring(l, msgBuf);
free(msgBuf);
return 2;
}
static int cmd_checksync(lua_State *l) {
lua_settop(l, 0); //[]
lua_getglobal(l, "branchtracks"); //[t]
assert(lua_type(l, -1) == LUA_TTABLE);
int notinsync = 0;
lua_pushnil(l); //[t_]
while(lua_next(l, -2) != 0) { //[tnt] | [t]
assert(lua_type(l, -2) == LUA_TNUMBER);
assert(lua_type(l, -1) == LUA_TTABLE);
lua_pushcfunction(l, checkTrackedBranch); //[tntf]
lua_pushvalue(l, -2); //[tntft]
lua_call(l, 1, 2); //[tntbs]
assert(lua_type(l, -2) == LUA_TBOOLEAN);
assert(lua_type(l, -1) == LUA_TSTRING);
if(lua_toboolean(l, -2) == 0) {
++notinsync;
}
printf("%s\n", lua_tostring(l, -1));
lua_pop(l, 3); //[tn]
}
//[t]
if(notinsync == 0) {
printf("\nAll repositories in sync\n");
} else {
printf("\n%d repositories not in sync\n", notinsync);
}
int rv = 0;
if(notinsync>0) rv = 1;
lua_pushinteger(l, rv); //[tn]
return 1;
}
static int cmd_unknown(lua_State *l) {
lua_settop(l, 1); //[s]
luaL_checkstring(l, 1);
return luaL_error(l, "unknown command: '%s'", lua_tostring(l, -1));
}
static int go(lua_State *l) {
lua_settop(l, 2); //[ss]
int rc = luaL_loadfilex(l, lua_tostring(l, 1), "t"); //[ssf] | [sss]
if(rc != LUA_OK) { //[sss]
return luaL_error(l, "Unable to load reposdef: '%s'",
lua_tostring(l, -1));
}
//[ssf]
lua_call(l, 0, 0); //[ss]
int nargs = 0;
if(strcmp(lua_tostring(l, 2), "fetchgit") == 0) {
lua_pushcfunction(l, cmd_fetchgit); //[ssf]
nargs = 0;
} else if(strcmp(lua_tostring(l, 2), "checksync") == 0) {
lua_pushcfunction(l, cmd_checksync); //[ssf]
nargs = 0;
} else {
lua_pushcfunction(l, cmd_unknown); //[ssf]
lua_pushvalue(l, 2); //[ssfs]
nargs = 1;
}
lua_call(l, nargs, 1); //[ssn]
assert(lua_type(l, -1) == LUA_TNUMBER);
return 1;
}
static void printusage(const char *prgname) {
fprintf(stderr, "Usage:\n\n%s -d <definition file> -c <command>\n",
prgname);
fprintf(stderr, "\nCommands:\n");
fprintf(stderr, "\tfetchgit: fetch all git repositories\n");
fprintf(stderr, "\tchecksync: check tags present in fossil repos\n");
exit(1);
}
int main(int argc, char **argv) {
lua_State *l = luaL_newstate(); //[]
lua_pushcfunction(l, go); //[f]
lua_settop(l, 3); //[f__]
int opt = 0;
while(1) {
opt = getopt(argc, argv, "d:c:");
if(opt == -1) break;
switch(opt) {
case 'c':
lua_pushstring(l, optarg); //[f??s]
lua_replace(l, 3); //[f?s]
break;
case 'd':
lua_pushstring(l, optarg); //[f??s]
lua_replace(l, 2); //[fs?]
break;
default:
lua_close(l);
printusage(argv[0]); //this bails
}
}
if(lua_type(l, 2) != LUA_TSTRING ||
lua_type(l, 3) != LUA_TSTRING) {
lua_close(l);
printusage(argv[0]); //this bails
}
int rc = lua_pcall(l, 2, 1, 0); //[n] | [s]
if(rc != LUA_OK) { //[s]
assert(lua_type(l, -1) == LUA_TSTRING);
fprintf(stderr, "Error: %s\n", lua_tostring(l, -1));
return 1;
}
assert(lua_type(l, -1) == LUA_TNUMBER);
rc = lua_tointeger(l, -1);
lua_close(l);
return rc;
}