/* * redir - simple round-robin based redirection cgi program */ #include #include #include #include #include #include #include #include #include #include #include /* * defines */ #define URL_FILE "redir.conf" #define STDIN 0 #define STDOUT 1 #define STDERR 2 #ifndef PAGE_SIZE # define PAGE_SIZE 4096 #endif #ifndef uint64_t # define uint64_t unsigned long long #endif #define NULLSTRING { .s = NULL, .l = 0, ._l = 0 } #define NULLSTRINGARRAY { .s = NULLSTRING, .a = NULL, .l = 0, ._l = 0 } /* * typedefs */ /* String type */ typedef struct { char *s; uint64_t l, _l; } String; /* Array of String */ typedef struct { String s, *a; uint64_t l, _l; } Stringarray; /* 1 if carriage return or linefeed */ int is_lineend (const char c) { if (c == '\r') return 1; if (c == '\n') return 1; return 0; } /* 1 if beginn of a comment is detected */ inline int is_comment (const char *c) { if (*c == '#') return 1; return 0; } /* uh oh, no memory */ inline void nomem () { write(STDERR, "out of memory! (this error message was also sent to the client)\n", 64); write(STDOUT, "Content-type: text/html\n\nredir: out of memory!\n", 47); exit(111); } /* allocates *p in quants of PAGE_SIZE */ void _realloc (void **p, size_t elemsize, uint64_t curlen, uint64_t *curbuflen, uint64_t addlen) { uint64_t newlen; newlen = (((curlen + addlen) * elemsize + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE; if (newlen == *curbuflen) return; *p = realloc(*p, newlen); if (!*p) nomem(); *curbuflen = newlen; } /* resize a String, honors NULL termination */ void str_realloc (String *s, const uint64_t l) { _realloc((void**) &s->s, sizeof(char), s->l, &s->_l, l + 1); } /* adds a C-String of given length */ void str_cat_n (String *s, const char *c, const uint64_t l) { str_realloc(s, l); memmove(s->s + s->l, c, l + 1); s->l += l; } /* adds a C-String */ void str_cat (String *s, const char *c) { str_cat_n(s, c, strlen(c)); } /* prints user error message, prints variable error message, and exits */ void die (const char *s1, const char *s2) { String s = NULLSTRING; str_cat(&s, s1); str_cat_n(&s, ": ", 2); str_cat(&s, s2); str_cat(&s, " (this error message was also sent to the client)\n"); write(STDERR, s.s, s.l); s.l = 0; str_cat(&s, "Content-type: text/html\n\nredir: generic error
\n"); str_cat(&s, s1); str_cat_n(&s, ": ", 2); str_cat(&s, s2); str_cat(&s, "
\nplease inform the administrator of this website\n"); write(STDERR, s.s, s.l); exit(1); } /* reads file into String */ inline void str_cat_file (String *s, const char *fn) { struct stat st; ssize_t ret; int fh = open(fn, O_RDONLY); if (fh == -1) die(fn, strerror(errno)); if (fstat(fh, &st) == -1) die("fstat()", strerror(errno)); str_realloc(s, st.st_size); ret = read(fh, s->s + s->l, st.st_size); if (ret == -1) die("read()", strerror(errno)); if ((unsigned) ret != st.st_size) die(fn, "cannot read whole file"); if (close(fh) == -1) die("close()", strerror(errno)); s->l += st.st_size; *(s->s + s->l) = '\0'; } /* resizes a Stringarray */ inline void array_realloc (Stringarray *a, const uint64_t l) { _realloc((void**) &a->a, sizeof(String), a->l, &a->_l, l); } /* reads all lines of a file into a Stringarray */ inline void array_cat_file (Stringarray *a, const char *fn) { String *s; char *l, *r; str_cat_file(&a->s, fn); for (r = a->s.s; *r;) { l = r; for (;; ++r) { if (!*r) return; if (is_lineend(*r)) break; } if (!is_comment(l)) { *r = '\0'; array_realloc(a, 1); s = &(a->a[a->l]); s->s = l; s->l = r - l; ++a->l; } for (++r; *r; ++r) if (!is_lineend(*r)) break; } } /* MAIN */ int main (int argc, char **argv) { Stringarray a = NULLSTRINGARRAY; String s = NULLSTRING; struct sembuf sem; key_t semkey, shmkey; int semid, shmid; void *shm; uint64_t counter; /* make compiler happy */ argc = argc; /* load url file */ array_cat_file(&a, URL_FILE); if (!a.l) die(URL_FILE, "no redirection target found"); /* create key for shared memory */ shmkey = ftok(argv[0], 2); if (shmkey == -1) die("ftok()", strerror(errno)); /* get (and maybe create) shared memory */ shmid = shmget(shmkey, sizeof(int), IPC_CREAT | 0660); if (shmid == -1) die("shmget()", strerror(errno)); /* map shared memory to this process memory */ shm = shmat(shmid, NULL, SHM_RND); if (shm == (void*) -1) die("shmat()", strerror(errno)); /* create key for semaphore */ semkey = ftok(argv[0], 1); if (semkey == -1) die("ftok()", strerror(errno)); /* try to create semaphore */ semid = semget(semkey, 1, IPC_CREAT | IPC_EXCL | 0660); if (semid == -1) { /* ohoh, creation failed */ if (errno != EEXIST) die("semget() (on create)", strerror(errno)); } else { /* we have created the semaphore -> initialize it */ sem.sem_num = 0; sem.sem_flg = 0; sem.sem_op = 1; if (semop(semid, &sem, 1) == -1) die("semop() (on init)", strerror(errno)); } /* get semaphore id */ semid = semget(semkey, 0, 0); if (semid == -1) die("semget() (on get)", strerror(errno)); /* enter critical section */ sem.sem_num = 0; sem.sem_flg = SEM_UNDO; sem.sem_op = -1; if (semop(semid, &sem, 1)) die("semop() (on lock)", strerror(errno)); /* mapped memory segment is our round robin counter */ /* shm is at least aligned to SHMLBA */ counter = *((uint64_t*) shm); /* sanitize counter */ if (counter >= a.l) counter = 0; /* store new value to shared memory */ *((uint64_t*) shm) = counter + 1; /* leave critical section */ sem.sem_num = 0; sem.sem_flg = SEM_UNDO; sem.sem_op = 1; if (semop(semid, &sem, 1)) die("semop() (on release)", strerror(errno)); /* print out location */ str_cat_n(&s, "Location: ", 10); str_cat_n(&s, a.a[counter].s, a.a[counter].l); str_cat_n(&s, "\n\n", 2); write(STDOUT, s.s, s.l); /* mapped shared memory segment released by OS on process exit */ return 0; }