ref: b41a69d4bd25da331cb771f743a4227b0f68e5c0
dir: /serve.c/
#include <u.h> #include <libc.h> #include <pool.h> #include "git.h" char *pathpfx = ""; int allowwrite; int fmtpkt(Conn *c, char *fmt, ...) { char pkt[Pktmax]; va_list ap; int n; va_start(ap, fmt); n = vsnprint(pkt, sizeof(pkt), fmt, ap); n = writepkt(c, pkt, n); va_end(ap); return n; } int showrefs(Conn *c) { int i, ret, nrefs; Hash head, *refs; char **names; ret = -1; nrefs = 0; if(resolveref(&head, "HEAD") != -1) { if(fmtpkt(c, "%H HEAD", head) == -1) goto error; } if((nrefs = listrefs(&refs, &names)) == -1) sysfatal("listrefs: %r"); for(i = 0; i < nrefs; i++) if(fmtpkt(c, "%H refs/%s\n", refs[i], names[i]) == -1) goto error; if(flushpkt(c) == -1) goto error; ret = 0; error: for(i = 0; i < nrefs; i++) free(names[i]); free(names); free(refs); return ret; } int negotiate(Conn *c, Hash **head, int *nhead, Hash **tail, int *ntail) { char pkt[Pktmax]; int n, acked; Object *o; if(showrefs(c) == -1) return -1; *head = nil; *tail = nil; *nhead = 0; *ntail = 0; acked = 0; while(1){ if((n = readpkt(c, pkt, sizeof(pkt))) == -1) goto error; if(n == 0) break; if(strncmp(pkt, "want ", 5) == 0){ *head = erealloc(*head, (*nhead + 1)*sizeof(Hash)); if(hparse(&(*head)[*nhead], &pkt[5]) == -1){ fmtpkt(c, "ERR: garbled want\n"); goto error; } *nhead += 1; } if(strncmp(pkt, "have ", 5) == 0){ *tail = erealloc(*tail, (*ntail + 1)*sizeof(Hash)); if(hparse(&(*tail)[*ntail], &pkt[5]) == -1){ fmtpkt(c, "ERR: garbled have\n"); goto error; } if((o = readobject((*tail)[*ntail])) == nil) continue; if(!acked) if(fmtpkt(c, "ACK %H\r\n", o->hash) == -1) goto error; unref(o); acked = 1; *ntail += 1; } } if(!acked) fmtpkt(c, "NAK\n"); return 0; error: free(*head); free(*tail); return -1; } int servpack(Conn *c) { Hash *head, *tail, h; Object **obj; int nhead, ntail, nobj; if(negotiate(c, &head, &nhead, &tail, &ntail) == -1) sysfatal("negotiate: %r"); dprint(1, "finding twixt\n"); if(findtwixt(head, nhead, tail, ntail, &obj, &nobj) == -1) sysfatal("twixt: %r"); dprint(1, "writing pack\n"); if(writepack(c->wfd, obj, nobj, &h) == -1) sysfatal("send: %r"); return 0; } int recvpack(Conn *c) { USED(c); sysfatal("recvpack: noimpl"); return -1; } char* parsecmd(char *buf, char *cmd, int ncmd) { int i; char *p; for(p = buf, i = 0; *p && i < ncmd - 1; i++, p++){ if(*p == ' ' || *p == '\t'){ cmd[i] = 0; break; } cmd[i] = *p; } while(*p == ' ' || *p == '\t') p++; return p; } void usage(void) { fprint(2, "usage: %s [-dw]\n", argv0); exits("usage"); } void main(int argc, char **argv) { char *p, cmd[32], buf[512], path[512]; Conn c; ARGBEGIN{ case 'd': debug++; chattygit++; break; case 'r': pathpfx = EARGF(usage()); if(*pathpfx != '/') sysfatal("path prefix must begin with '/'"); break; case 'w': allowwrite++; break; default: usage(); break; }ARGEND; gitinit(); initconn(&c, 0, 1); if(readpkt(&c, buf, sizeof(buf)) == -1) sysfatal("readpkt: %r"); p = parsecmd(buf, cmd, sizeof(cmd)); if(snprint(path, sizeof(path), "%s/%s", pathpfx, p) == sizeof(path)) sysfatal("%s: path too long\n", p); if(chdir(path) == -1) sysfatal("cd %s: %r", p); if(access(".git", AREAD) == -1) sysfatal("no git repository"); if(strcmp(cmd, "git-fetch-pack") == 0 && allowwrite) recvpack(&c); else if(strcmp(cmd, "git-upload-pack") == 0) servpack(&c); else sysfatal("unsupported command '%s'", cmd); exits(nil); }