main.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <errno.h>
  6. #include <sys/stat.h>
  7. #include "cproc.h"
  8. #include "../src/sti/sti.h"
  9. #include "../src/git.h"
  10. #include "../src/sys.h"
  11. #define p(...) \
  12. do {\
  13. fprintf(f, __VA_ARGS__); \
  14. fflush(f); \
  15. } while(0);
  16. enum {
  17. NONE, PUSH, PULL,
  18. };
  19. FILE* f;
  20. int hexdigit(char c) {
  21. if(c >= '0' && c <= '9') return c - '0';
  22. if(c >= 'a' && c <= 'f') return c - 'a' + 10;
  23. if(c >= 'A' && c <= 'F') return c - 'A' + 10;
  24. return 0;
  25. }
  26. int read_line_len(char* buf) {
  27. int len = 0;
  28. len += hexdigit(buf + 3) * 0x1;
  29. len += hexdigit(buf + 2) * 0x10;
  30. len += hexdigit(buf + 1) * 0x100;
  31. len += hexdigit(buf + 0) * 0x1000;
  32. return len;
  33. }
  34. /*
  35. char get_file_type(char* path) {
  36. struct stat st;
  37. int res = stat(path, &st);
  38. if(res) return 0;
  39. if(st.st_mode & S_IFDIR) return 'd';
  40. if(st.st_mode & S_IFREG) return 'f';
  41. if(st.st_mode & S_IFLNK) return 'l';
  42. return 'o';
  43. }//*/
  44. /*
  45. int is_dir(char* path) {
  46. return 'd' == get_file_type(path);
  47. }
  48. /*
  49. int is_file(char* path) {
  50. return 'f' == get_file_type(path);
  51. }
  52. int file_doesnt_exist(char* path) {
  53. return 0 == get_file_type(path);
  54. }
  55. */
  56. int main(int argc, char* argv[]) {
  57. char* git_path = NULL;
  58. unlink("/tmp/lol.txt");
  59. f = fopen("/tmp/lol.txt", "wb");
  60. // FILE* f2 = fopen("/tmp/wut.txt", "wb");
  61. if(!f ) {
  62. printf("could not open log file\n");
  63. exit(2);
  64. }
  65. for(int i = 0; i < argc; i++) {
  66. fprintf(f, "%d: %s\n", i, argv[i]);
  67. }
  68. fflush(f);
  69. int mode = NONE;
  70. if(!strncmp(argv[2], "git-receive-pack", strlen("git-receive-pack"))) mode = PUSH;
  71. else if(!strncmp(argv[2], "git-upload-pack", strlen("git-upload-pack"))) mode = PULL;
  72. if(mode == NONE) {
  73. p("unknown mode: %s\n", argv[2]);
  74. exit(1);
  75. }
  76. char* raw = strchr(argv[2], ' ');
  77. raw += strspn(raw, " ");
  78. if(!raw) {
  79. p("missing repo path\n");
  80. exit(1);
  81. }
  82. if(raw[0] == '\'') {
  83. raw++;
  84. int len = strlen(raw);
  85. if(raw[len - 1] == '\'') raw[len - 1] = 0;
  86. }
  87. if(raw[0] == '~') raw++;
  88. /*
  89. URL Format:
  90. git@server:user/meta
  91. git@server:user/repo.git
  92. git@server:user/repo/[meta|issues|pulls]
  93. */
  94. char* syspath = getenv("GITVIEWER_BASE_DIR");
  95. if(!syspath) {
  96. p("no GitViewer base path in ENV\n");
  97. exit(1);
  98. }
  99. char** parts = strsplit(raw, '/', NULL);
  100. // grab and validate user
  101. int goodchars = strspn(parts[0], "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_");
  102. if(strlen(parts[0]) != goodchars) {
  103. p("invalid username: %s\n", parts[0]);
  104. exit(1);
  105. }
  106. char* username = parts[0];
  107. char* user_dir = path_join(syspath, "users", username);
  108. if(!is_dir(user_dir)) {
  109. p("user %s does not exist\n", username);
  110. exit(1);
  111. }
  112. // grab and verify the repo name
  113. goodchars = strspn(parts[1], "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.");
  114. if(strlen(parts[1]) != goodchars) {
  115. p("invalid repo name: %s\n", raw);
  116. exit(1);
  117. }
  118. char* reponame = parts[1];
  119. if(parts[2] == NULL) {
  120. char* ext = strrchr(reponame, '.');
  121. if(ext && !strcmp(ext, ".git")) {
  122. ext[0] = 0;
  123. git_path = path_join(user_dir, "repos", reponame, "src.git");
  124. if(!is_dir(git_path)) {
  125. p("'%s' is not a valid git repo\n", git_path);
  126. exit(1);
  127. }
  128. // verify permissions with SSH-provided username
  129. char* ssh_username = getenv("GITVIEWER_SSH_USER");
  130. if(!ssh_username) {
  131. p("no username key provided by ssh\n");
  132. exit(1);
  133. }
  134. if(mode == PUSH) {
  135. if(0 != strcmp(ssh_username, username)) {
  136. git_repo gr = {
  137. .abs_src_path = path_join(user_dir, "repos", reponame, "settings"),
  138. };
  139. char* pushers_src = git_get_file(&gr, "master", "pushers");
  140. char** pushers = strsplit_inplace("\n", pushers_src, NULL);
  141. int allowed = 0;
  142. for(int i = 0; pushers[i]; i++) {
  143. if(!strcasecmp(ssh_username, pushers[i])) {
  144. allowed = 1;
  145. break;
  146. }
  147. }
  148. if(!allowed) {
  149. p("provided username and ssh username do not match: %s != %s\n", username, ssh_username);
  150. exit(1);
  151. }
  152. }
  153. }
  154. }
  155. else {
  156. }
  157. }
  158. else {
  159. char* repo_dir = path_join(user_dir, "repos", reponame);
  160. if(!is_dir(repo_dir)) {
  161. p("user/repo %s/%s does not exist\n", username, reponame);
  162. exit(1);
  163. }
  164. // figure out what they are trying to do
  165. if(!strcmp(raw, "src.git")) { // regular git access
  166. git_path = path_join(repo_dir, "src.git");
  167. if(!is_dir(git_path)) {
  168. p("'%s' is not a valid git repo\n", git_path);
  169. exit(1);
  170. }
  171. }
  172. else {
  173. p("operation '%s' not supported\n", raw);
  174. exit(1);
  175. }
  176. }
  177. if(!git_path) {
  178. p("no git repo found\n");
  179. exit(1);
  180. }
  181. int BUFSZ = 8192;
  182. char buf[BUFSZ];
  183. //
  184. // fprintf(f, "ttyname: %s\n", ttyname(STDOUT_FILENO));
  185. // fflush(f);
  186. //
  187. // int mypty = open(ttyname(STDOUT_FILENO), O_RDWR);
  188. // if(mypty == -1) {
  189. // fprintf(f, "\n tty open error: %s\n", strerror(errno));
  190. // fflush(f);
  191. // exit(1);
  192. // }
  193. //
  194. fcntl(STDOUT_FILENO, F_SETFL, fcntl(STDOUT_FILENO, F_GETFL) | O_NONBLOCK);
  195. fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
  196. // fcntl(mypty, F_SETFL, fcntl(mypty, F_GETFL) | O_NONBLOCK);
  197. char* args[3];
  198. args[2] = NULL;
  199. args[1] = git_path;
  200. args[0] = mode == PULL ? "git-upload-pack" : "git-receive-pack";
  201. p("running %s %s\n", args[0], args[1]);
  202. struct child_process_info* cpi = exec_process_pipe(args[0], args);
  203. char* line = malloc(1024);
  204. int lineLen = 0;
  205. int packline_len = 0;
  206. int limit = 1000;
  207. while(1) {
  208. // fprintf(f, "pre stdin read\n");
  209. // fflush(f);
  210. int bw;
  211. int totalWritten = 0;
  212. do {
  213. p("waiting to read\n");
  214. bw = read(cpi->child_stdout, buf, BUFSZ);
  215. p("read %d\n", bw)
  216. if(bw > 0) {
  217. totalWritten += bw;
  218. memcpy(line + lineLen, buf, bw);
  219. lineLen += bw;
  220. line[lineLen] = 0;
  221. if(packline_len == 0) {
  222. if(lineLen >= 4) {
  223. packline_len = read_line_len(line);
  224. p("packline len: %d: \n", packline_len);
  225. }
  226. }
  227. if(packline_len && lineLen >= packline_len) {
  228. p("packline (len: %d): \n", packline_len);
  229. memmove(line, line + packline_len, lineLen - packline_len);
  230. lineLen -= packline_len;
  231. line[lineLen] = 0;
  232. packline_len = 0;
  233. }
  234. fwrite(buf, 1, bw, f);
  235. p("\n%d bytes read from the child pty\n", bw);
  236. p("pre stdout write of %d bytes\n", bw);
  237. /*
  238. for(int j = 0; j < bw; j++) {
  239. if(EOF == putchar(buf[j])) {
  240. fprintf(f, "\nputchar write error: %s\n", strerror(errno));
  241. fflush(f);
  242. exit(1);
  243. }
  244. }*/
  245. int bww = 0;
  246. while(bww < bw) {
  247. bww += fwrite(buf + bww, 1, bw - bww, stdout);
  248. }
  249. // int bww = write(STDOUT_FILENO, buf, bw);
  250. // if(bww < 0) {
  251. // fprintf(f, "\nstdout write error: %s\n", strerror(errno));
  252. // fflush(f);
  253. // break;
  254. // }
  255. p("(%d bytes written)\n", bw);
  256. // fsync(STDOUT_FILENO);
  257. // fprintf(f, "flushing finished\n");
  258. // fflush(f);
  259. }
  260. else if(bw < 0) {
  261. p("less than one\n")
  262. if(errno == EIO) {
  263. p("\npty IO Ferror: %s\n", strerror(errno));
  264. // fflush(f);
  265. }
  266. else if(errno != EAGAIN && errno != EWOULDBLOCK) {
  267. p("\npty read Verror: %s\n", strerror(errno));
  268. exit(1);
  269. }
  270. else if(errno == EAGAIN) {
  271. p("EAGAIN");
  272. break;
  273. }
  274. p("Zerror: %d - %s\n", errno, strerror(errno))
  275. break;
  276. }
  277. else {
  278. p("zero bytes \n");
  279. }
  280. } while(bw != 0);
  281. if(totalWritten > 0) {
  282. p("flushing \n");
  283. fflush(stdout);
  284. p("flushing finished\n");
  285. }
  286. int br = read(STDIN_FILENO, buf, BUFSZ);
  287. if(br > 0) {
  288. fwrite(buf, 1, br, f);
  289. p("\n%d bytes read from shell stdin\n",br);
  290. p("pre pty write\n");
  291. int brr = 0;
  292. while(brr < br) {
  293. brr += write(cpi->child_stdin, buf + brr, br - brr);
  294. }
  295. fsync(cpi->child_stdin);
  296. p("postwrite, pre pty read\n");
  297. }
  298. else if(br < 0) {
  299. if(errno == EIO) {
  300. // p("stdin read Werror: %d - %s\n", errno, strerror(errno))
  301. }
  302. else if(errno != EAGAIN) {
  303. p("\nstdin read Qerror: %s\n", strerror(errno));
  304. break;
  305. }
  306. }
  307. // fprintf(f, "checking\n");
  308. // fflush(f);
  309. int status;
  310. int pid = waitpid(cpi->pid, &status, WNOHANG);
  311. if(pid != 0) {
  312. p("[pid = %d]exiting with status %d\n", pid, WEXITSTATUS(status));
  313. break;
  314. }
  315. // fprintf(f, "done checking\n");
  316. // fflush(f);
  317. // usleep(10);
  318. // exit(1);
  319. // fprintf(f, ".");
  320. // fflush(f);
  321. if(0 && --limit <= 0) {
  322. p("limit hit\n");
  323. break;
  324. }
  325. }
  326. p("done\n");
  327. fclose(f);
  328. kill(cpi->pid, SIGKILL);
  329. return 0;
  330. }