Remaining files for the videos, minus the statserve final.

This commit is contained in:
lcthw 2015-08-23 19:55:31 -07:00
parent 026a100a92
commit 4305a58bb9
124 changed files with 4982 additions and 0 deletions

5
.gitignore vendored
View File

@ -1,3 +1,8 @@
Videos
.*.sw*
*.pdf
*.dSYM
*.so
*.o
*.log
*.db

4
Makefile Normal file
View File

@ -0,0 +1,4 @@
# basic simple Makefile starter
#
CFLAGS=-Wall -g

39
dbg.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr,\
"[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr,\
"[WARN] (%s:%d: errno: %s) " M "\n",\
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) {\
log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\
errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
errno=0; goto error; }
#endif

12
ex1/ex1_zed.c Normal file
View File

@ -0,0 +1,12 @@
#include <stdio.h>
/* This is a comment. */
int main(int argc, char *argv[])
{
int distance = 100;
// this is also a comment
printf("You are %d miles away.\n");
return 0;
}

2
ex10/Makefile Normal file
View File

@ -0,0 +1,2 @@
CFLAGS=-Wall -g

2
ex11/Makefile Normal file
View File

@ -0,0 +1,2 @@
CFLAGS=-Wall -g

2
ex12/Makefile Normal file
View File

@ -0,0 +1,2 @@
CFLAGS=-Wall -g

2
ex13/Makefile Normal file
View File

@ -0,0 +1,2 @@
CFLAGS=-Wall -g

2
ex14/Makefile Normal file
View File

@ -0,0 +1,2 @@
CFLAGS=-Wall -g

2
ex15/Makefile Normal file
View File

@ -0,0 +1,2 @@
CFLAGS=-Wall -g

2
ex16/Makefile Normal file
View File

@ -0,0 +1,2 @@
CFLAGS=-Wall -g

2
ex17/Makefile Normal file
View File

@ -0,0 +1,2 @@
CFLAGS=-Wall -g

2
ex18/Makefile Normal file
View File

@ -0,0 +1,2 @@
CFLAGS=-Wall -g

2
ex19/Makefile Normal file
View File

@ -0,0 +1,2 @@
CFLAGS=-Wall -g

39
ex19/dbg.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr,\
"[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr,\
"[WARN] (%s:%d: errno: %s) " M "\n",\
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) {\
log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\
errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
errno=0; goto error; }
#endif

7
ex2/Makefile Normal file
View File

@ -0,0 +1,7 @@
CFLAGS=-Wall -g
all: ex1
clean:
rm -f ex1

12
ex2/ex1.c Normal file
View File

@ -0,0 +1,12 @@
#include <stdio.h>
/* This is a comment. */
int main(int argc, char *argv[])
{
int distance = 100;
// this is also a comment
printf("You are %d miles away.\n", distance);
return 0;
}

2
ex20/Makefile Normal file
View File

@ -0,0 +1,2 @@
CFLAGS=-Wall -g

39
ex20/dbg.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr,\
"[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr,\
"[WARN] (%s:%d: errno: %s) " M "\n",\
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) {\
log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\
errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
errno=0; goto error; }
#endif

112
ex20/ex20_obo.c Normal file
View File

@ -0,0 +1,112 @@
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
/** Our old friend die from ex17. */
void die(const char *message)
{
if (errno) {
perror(message);
} else {
printf("ERROR: %s\n", message);
}
exit(1);
}
// a typedef creates a fake type, in this
// case for a function pointer
typedef int (*compare_cb) (int a, int b);
/**
* A classic bubble sort function that uses the
* compare_cb to do the sorting.
*/
int *bubble_sort(int *numbers, int count, compare_cb cmp)
{
int temp = 0;
int i = 0;
int j = 0;
int *target = malloc(count * sizeof(int));
if (!target)
die("Memory error.");
memcpy(target, numbers, count * sizeof(int));
for (i = 0; i < count; i++) {
for (j = 0; j < count - 1; j++) {
if (cmp(target[j], target[j + 1]) > 0) {
temp = target[j + 1];
target[j + 1] = target[j];
target[j] = temp;
}
}
}
return target;
}
int sorted_order(int a, int b)
{
return a - b;
}
int reverse_order(int a, int b)
{
return b - a;
}
int strange_order(int a, int b)
{
if (a == 0 || b == 0) {
return 0;
} else {
return a % b;
}
}
/**
* Used to test that we are sorting things correctly
* by doing the sort and printing it out.
*/
void test_sorting(int *numbers, int count, compare_cb cmp)
{
int i = 0;
int *sorted = bubble_sort(numbers, count, cmp);
if (!sorted)
die("Failed to sort as requested.");
for (i = 0; i < count; i++) {
printf("%d ", sorted[i]);
}
printf("\n");
free(sorted);
}
int main(int argc, char *argv[])
{
if (argc < 2) die("USAGE: ex18 4 3 1 5 6");
int count = argc - 1;
int i = 0;
char **inputs = argv + 1;
int *numbers = malloc(count * sizeof(int));
if (!numbers) die("Memory error.");
for (i = 0; i < count; i++) {
numbers[i] = atoi(inputs[i]);
}
test_sorting(numbers, count, sorted_order);
test_sorting(numbers, count, reverse_order);
test_sorting(numbers, count, strange_order);
free(numbers);
return 0;
}

3
ex20/test.sh Normal file
View File

@ -0,0 +1,3 @@
rm ex20
make ex20
./ex20 1 82 39 44 1 2 3 4 9 8 01 10 11

4
ex21/Makefile Normal file
View File

@ -0,0 +1,4 @@
CFLAGS=-Wall -g
all:
cc $(CFLAGS) -o ex21 ex21.c ex21_main.c

2
ex22/Makefile Normal file
View File

@ -0,0 +1,2 @@
CFLAGS=-Wall -g

39
ex22/dbg.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr,\
"[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr,\
"[WARN] (%s:%d: errno: %s) " M "\n",\
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) {\
log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\
errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
errno=0; goto error; }
#endif

2
ex23/Makefile Normal file
View File

@ -0,0 +1,2 @@
CFLAGS=-Wall -g

39
ex23/dbg.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr,\
"[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr,\
"[WARN] (%s:%d: errno: %s) " M "\n",\
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) {\
log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\
errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
errno=0; goto error; }
#endif

2
ex24/Makefile Normal file
View File

@ -0,0 +1,2 @@
CFLAGS=-Wall -g

39
ex24/dbg.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr,\
"[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr,\
"[WARN] (%s:%d: errno: %s) " M "\n",\
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) {\
log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\
errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
errno=0; goto error; }
#endif

2
ex25/Makefile Normal file
View File

@ -0,0 +1,2 @@
CFLAGS=-Wall -g

39
ex25/dbg.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr,\
"[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr,\
"[WARN] (%s:%d: errno: %s) " M "\n",\
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) {\
log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\
errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
errno=0; goto error; }
#endif

6
ex26/logfind.1/Makefile Normal file
View File

@ -0,0 +1,6 @@
CFLAGS=-Wall -g
all: logfind
./logfind || true
./logfind test test test

39
ex26/logfind.1/dbg.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr,\
"[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr,\
"[WARN] (%s:%d: errno: %s) " M "\n",\
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) {\
log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\
errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
errno=0; goto error; }
#endif

12
ex26/logfind.1/logfind.c Normal file
View File

@ -0,0 +1,12 @@
#include "dbg.h"
int main(int argc, char *argv[])
{
check(argc > 2, "USAGE: logfind word word word");
return 0;
error:
return 1;
}

8
ex26/logfind.2/Makefile Normal file
View File

@ -0,0 +1,8 @@
CFLAGS=-Wall -g
all: logfind
./logfind || true
./logfind error
clean:
rm -f logfind

39
ex26/logfind.2/dbg.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr,\
"[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr,\
"[WARN] (%s:%d: errno: %s) " M "\n",\
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) {\
log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\
errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
errno=0; goto error; }
#endif

50
ex26/logfind.2/logfind.c Normal file
View File

@ -0,0 +1,50 @@
#include "dbg.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const size_t MAX_LINE = 1024;
int scan_file(const char *filename, int search_len, char *search_for[])
{
char *line = calloc(MAX_LINE, 1);
FILE *file = fopen(filename, "r");
char *found = NULL;
int i = 0;
check_mem(line);
check(file, "Failed to open file: %s", filename);
// read each line of the file and search that line for the contents
while(fgets(line, MAX_LINE-1, file) != NULL && found == NULL) {
for(i = 0; i < search_len && found == NULL; i++) {
found = strcasestr(line, search_for[i]);
if(found) {
printf("%s\n", filename);
}
}
}
free(line);
fclose(file);
return 0;
error:
if(line) free(line);
if(file) fclose(file);
return -1;
}
int main(int argc, char *argv[])
{
check(argc > 1, "USAGE: logfind word word word");
scan_file("logfind.c", argc, argv);
return 0;
error:
return 1;
}

3
ex26/logfind.3/.logfind Normal file
View File

@ -0,0 +1,3 @@
*.c
*.h
Makefile

8
ex26/logfind.3/Makefile Normal file
View File

@ -0,0 +1,8 @@
CFLAGS=-Wall -g
all: logfind
./logfind || true
./logfind error
clean:
rm -f logfind

39
ex26/logfind.3/dbg.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr,\
"[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr,\
"[WARN] (%s:%d: errno: %s) " M "\n",\
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) {\
log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\
errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
errno=0; goto error; }
#endif

87
ex26/logfind.3/logfind.c Normal file
View File

@ -0,0 +1,87 @@
#include "dbg.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glob.h>
const size_t MAX_LINE = 1024;
int list_files(glob_t *pglob)
{
char *line = calloc(MAX_LINE, 1);
FILE *file = fopen(".logfind", "r");
int glob_flags = GLOB_TILDE;
int i = 0;
int rc = -1;
check(pglob != NULL, "Invalid glob_t given.");
check_mem(line);
check(file, "Failed to open .logfind. Make that first.");
rc = glob("*.h", glob_flags, NULL, pglob);
check(rc == 0, "Failed to glob.");
rc = glob("*.c", glob_flags | GLOB_APPEND, NULL, pglob);
check(rc == 0, "Failed to glob.");
for(i = 0; i < pglob->gl_pathc; i++) {
debug("Matched file: %s", pglob->gl_pathv[i]);
}
rc = 0; // all good
error: // fallthrough
if(line) free(line);
return rc;
}
int scan_file(const char *filename, int search_len, char *search_for[])
{
char *line = calloc(MAX_LINE, 1);
FILE *file = fopen(filename, "r");
char *found = NULL;
int i = 0;
check_mem(line);
check(file, "Failed to open file: %s", filename);
// read each line of the file and search that line for the contents
while(fgets(line, MAX_LINE-1, file) != NULL && found == NULL) {
for(i = 0; i < search_len && found == NULL; i++) {
found = strcasestr(line, search_for[i]);
if(found) {
printf("%s\n", filename);
}
}
}
free(line);
fclose(file);
return 0;
error:
if(line) free(line);
if(file) fclose(file);
return -1;
}
int main(int argc, char *argv[])
{
int i = 0;
glob_t files_found;
check(argc > 1, "USAGE: logfind word word word");
check(list_files(&files_found) == 0, "Failed to list files.");
for(i = 0; i < files_found.gl_pathc; i++) {
scan_file(files_found.gl_pathv[i], argc, argv);
}
globfree(&files_found);
return 0;
error:
return 1;
}

3
ex26/logfind.4/.logfind Normal file
View File

@ -0,0 +1,3 @@
*.c
*.h
Makefile

8
ex26/logfind.4/Makefile Normal file
View File

@ -0,0 +1,8 @@
CFLAGS=-Wall -g
all: logfind
./logfind || true
./logfind MAX_LINE
clean:
rm -f logfind

39
ex26/logfind.4/dbg.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr,\
"[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr,\
"[WARN] (%s:%d: errno: %s) " M "\n",\
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) {\
log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\
errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
errno=0; goto error; }
#endif

94
ex26/logfind.4/logfind.c Normal file
View File

@ -0,0 +1,94 @@
#define NDEBUG
#include "dbg.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glob.h>
const size_t MAX_LINE = 1024;
int list_files(glob_t *pglob)
{
char *line = calloc(MAX_LINE, 1);
FILE *file = fopen(".logfind", "r");
int glob_flags = GLOB_TILDE;
int i = 0;
int rc = -1;
check(pglob != NULL, "Invalid glob_t given.");
check_mem(line);
check(file, "Failed to open .logfind. Make that first.");
while(fgets(line, MAX_LINE-1, file) != NULL) {
line[strlen(line) - 1] = '\0'; // drop the \n ending
debug("Globbing %s", line);
rc = glob(line, glob_flags, NULL, pglob);
check(rc == 0 || rc == GLOB_NOMATCH, "Failed to glob.");
// dumb work around to a stupid design in glob
if(glob_flags == GLOB_TILDE) glob_flags |= GLOB_APPEND;
}
for(i = 0; i < pglob->gl_pathc; i++) {
debug("Matched file: %s", pglob->gl_pathv[i]);
}
rc = 0; // all good
error: // fallthrough
if(line) free(line);
return rc;
}
int scan_file(const char *filename, int search_len, char *search_for[])
{
char *line = calloc(MAX_LINE, 1);
FILE *file = fopen(filename, "r");
char *found = NULL;
int i = 0;
check_mem(line);
check(file, "Failed to open file: %s", filename);
// read each line of the file and search that line for the contents
while(fgets(line, MAX_LINE-1, file) != NULL && found == NULL) {
for(i = 0; i < search_len && found == NULL; i++) {
found = strcasestr(line, search_for[i]);
if(found) {
printf("%s\n", filename);
}
}
}
free(line);
fclose(file);
return 0;
error:
if(line) free(line);
if(file) fclose(file);
return -1;
}
int main(int argc, char *argv[])
{
int i = 0;
glob_t files_found;
check(argc > 1, "USAGE: logfind word word word");
check(list_files(&files_found) == 0, "Failed to list files.");
for(i = 0; i < files_found.gl_pathc; i++) {
scan_file(files_found.gl_pathv[i], argc, argv);
}
globfree(&files_found);
return 0;
error:
return 1;
}

3
ex26/logfind.5/.logfind Normal file
View File

@ -0,0 +1,3 @@
*.c
*.h
Makefile

10
ex26/logfind.5/Makefile Normal file
View File

@ -0,0 +1,10 @@
CFLAGS=-Wall -g
all: logfind
./logfind || true
./logfind MAX_LINE
./logfind error MAX LINE
./logfind -o error MAX LINE
clean:
rm -f logfind

39
ex26/logfind.5/dbg.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr,\
"[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr,\
"[WARN] (%s:%d: errno: %s) " M "\n",\
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) {\
log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\
errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
errno=0; goto error; }
#endif

139
ex26/logfind.5/logfind.c Normal file
View File

@ -0,0 +1,139 @@
#define NDEBUG
#include "dbg.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glob.h>
const size_t MAX_LINE = 1024;
int list_files(glob_t *pglob)
{
char *line = calloc(MAX_LINE, 1);
FILE *file = fopen(".logfind", "r");
int glob_flags = GLOB_TILDE;
int i = 0;
int rc = -1;
check(pglob != NULL, "Invalid glob_t given.");
check_mem(line);
check(file, "Failed to open .logfind. Make that first.");
while(fgets(line, MAX_LINE-1, file) != NULL) {
line[strlen(line) - 1] = '\0'; // drop the \n ending
debug("Globbing %s", line);
rc = glob(line, glob_flags, NULL, pglob);
check(rc == 0 || rc == GLOB_NOMATCH, "Failed to glob.");
// dumb work around to a stupid design in glob
if(glob_flags == GLOB_TILDE) glob_flags |= GLOB_APPEND;
}
for(i = 0; i < pglob->gl_pathc; i++) {
debug("Matched file: %s", pglob->gl_pathv[i]);
}
rc = 0; // all good
error: // fallthrough
if(line) free(line);
return rc;
}
int found_it(int use_or, int found_count, int search_len)
{
debug("use_or: %d, found_count: %d, search_len: %d", use_or, found_count, search_len);
if(use_or && found_count > 0) {
return 1;
} else if(!use_or && found_count == search_len) {
return 1;
} else {
return 0;
}
}
int scan_file(const char *filename, int use_or, int search_len, char *search_for[])
{
char *line = calloc(MAX_LINE, 1);
FILE *file = fopen(filename, "r");
int found_count = 0;
int i = 0;
check_mem(line);
check(file, "Failed to open file: %s", filename);
// read each line of the file and search that line for the contents
while(fgets(line, MAX_LINE-1, file) != NULL)
{
for(i = 0; i < search_len; i++) {
if(strcasestr(line, search_for[i]) != NULL) {
debug("file: %s, line: %s, search: %s", filename, line, search_for[i]);
found_count++;
}
}
if(found_it(use_or, found_count, search_len)) {
printf("%s\n", filename);
break;
} else {
found_count = 0;
}
}
free(line);
fclose(file);
return 0;
error:
if(line) free(line);
if(file) fclose(file);
return -1;
}
int parse_args(int *use_or, int *argc, char **argv[])
{
(*argc)--;
(*argv)++;
if(strcmp((*argv)[0], "-o") == 0) {
*use_or = 1;
(*argc)--; // skip the -o
(*argv)++;
check(*argc > 1, "You need words after -o.");
} else {
use_or = 0;
}
return 0;
error:
return -1;
}
int main(int argc, char *argv[])
{
int i = 0;
int use_or = 0;
glob_t files_found;
check(argc > 1, "USAGE: logfind [-o] words");
check(parse_args(&use_or, &argc, &argv) == 0, "USAGE: logfind [-o] words");
check(list_files(&files_found) == 0, "Failed to list files.");
for(i = 0; i < files_found.gl_pathc; i++) {
scan_file(files_found.gl_pathv[i], use_or, argc, argv);
}
globfree(&files_found);
return 0;
error:
return 1;
}

3
ex27/logfind.5/.logfind Normal file
View File

@ -0,0 +1,3 @@
*.c
*.h
Makefile

10
ex27/logfind.5/Makefile Normal file
View File

@ -0,0 +1,10 @@
CFLAGS=-Wall -g
all: logfind
./logfind || true
./logfind MAX_LINE
./logfind error MAX LINE
./logfind -o error MAX LINE
clean:
rm -f logfind

39
ex27/logfind.5/dbg.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr,\
"[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr,\
"[WARN] (%s:%d: errno: %s) " M "\n",\
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) {\
log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\
errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
errno=0; goto error; }
#endif

143
ex27/logfind.5/logfind.c Normal file
View File

@ -0,0 +1,143 @@
#define NDEBUG
#include "dbg.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glob.h>
#include <assert.h>
const size_t MAX_LINE = 1024;
int list_files(glob_t *pglob)
{
char *line = calloc(MAX_LINE, 1);
FILE *file = fopen(".logfind", "r");
int glob_flags = GLOB_TILDE;
int i = 0;
int rc = -1;
check(pglob != NULL, "Invalid glob_t given.");
check_mem(line);
check(file, "Failed to open .logfind. Make that first.");
while(fgets(line, MAX_LINE-1, file) != NULL) {
size_t line_length = strnlen(line, MAX_LINE - 1);
assert(line_length < MAX_LINE && "Got a line length too long.");
line[line_length] = '\0'; // drop the \n ending
debug("Globbing %s", line);
rc = glob(line, glob_flags, NULL, pglob);
check(rc == 0 || rc == GLOB_NOMATCH, "Failed to glob.");
// dumb work around to a stupid design in glob
if(glob_flags == GLOB_TILDE) glob_flags |= GLOB_APPEND;
}
for(i = 0; i < pglob->gl_pathc; i++) {
debug("Matched file: %s", pglob->gl_pathv[i]);
}
rc = 0; // all good
error: // fallthrough
if(line) free(line);
return rc;
}
int found_it(int use_or, int found_count, int search_len)
{
debug("use_or: %d, found_count: %d, search_len: %d", use_or, found_count, search_len);
if(use_or && found_count > 0) {
return 1;
} else if(!use_or && found_count == search_len) {
return 1;
} else {
return 0;
}
}
int scan_file(const char *filename, int use_or, int search_len, char *search_for[])
{
char *line = calloc(MAX_LINE, 1);
FILE *file = fopen(filename, "r");
int found_count = 0;
int i = 0;
check_mem(line);
check(file, "Failed to open file: %s", filename);
// read each line of the file and search that line for the contents
while(fgets(line, MAX_LINE-1, file) != NULL)
{
for(i = 0; i < search_len; i++) {
if(strcasestr(line, search_for[i]) != NULL) {
debug("file: %s, line: %s, search: %s", filename, line, search_for[i]);
found_count++;
}
}
if(found_it(use_or, found_count, search_len)) {
printf("%s\n", filename);
break;
} else {
found_count = 0;
}
}
free(line);
fclose(file);
return 0;
error:
if(line) free(line);
if(file) fclose(file);
return -1;
}
int parse_args(int *use_or, int *argc, char **argv[])
{
(*argc)--;
(*argv)++;
if(strcmp((*argv)[0], "-o") == 0) {
*use_or = 1;
(*argc)--; // skip the -o
(*argv)++;
check(*argc > 1, "You need words after -o.");
} else {
*use_or = 0;
}
return 0;
error:
return -1;
}
int main(int argc, char *argv[])
{
int i = 0;
int use_or = 1;
glob_t files_found;
check(argc > 1, "USAGE: logfind [-o] words");
check(parse_args(&use_or, &argc, &argv) == 0, "USAGE: logfind [-o] words");
check(list_files(&files_found) == 0, "Failed to list files.");
for(i = 0; i < files_found.gl_pathc; i++) {
scan_file(files_found.gl_pathv[i], use_or, argc, argv);
}
globfree(&files_found);
return 0;
error:
return 1;
}

39
ex29/dbg.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr,\
"[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr,\
"[WARN] (%s:%d: errno: %s) " M "\n",\
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) {\
log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\
errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
errno=0; goto error; }
#endif

5
ex3/Makefile Normal file
View File

@ -0,0 +1,5 @@
CFLAGS=-Wall -g
clean:
rm -f ex3

0
ex48b/c-skeleton/LICENSE Normal file
View File

55
ex48b/c-skeleton/Makefile Normal file
View File

@ -0,0 +1,55 @@
CFLAGS=-g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG $(OPTFLAGS)
LIBS=-ldl $(OPTLIBS)
PREFIX?=/usr/local
SOURCES=$(wildcard src/**/*.c src/*.c)
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
TEST_SRC=$(wildcard tests/*_tests.c)
TESTS=$(patsubst %.c,%,$(TEST_SRC))
TARGET=build/libYOUR_LIBRARY.a
SO_TARGET=$(patsubst %.a,%.so,$(TARGET))
# The Target Build
all: $(TARGET) $(SO_TARGET) tests
dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS)
dev: all
$(TARGET): CFLAGS += -fPIC
$(TARGET): build $(OBJECTS)
ar rcs $@ $(OBJECTS)
ranlib $@
$(SO_TARGET): $(TARGET) $(OBJECTS)
$(CC) -shared -o $@ $(OBJECTS)
build:
@mkdir -p build
@mkdir -p bin
# The Unit Tests
.PHONY: tests
tests: CFLAGS += $(TARGET)
tests: $(TESTS)
sh ./tests/runtests.sh
# The Cleaner
clean:
rm -rf build $(OBJECTS) $(TESTS)
rm -f tests/tests.log
find . -name "*.gc*" -exec rm {} \;
rm -rf `find . -name "*.dSYM" -print`
# The Install
install: all
install -d $(DESTDIR)/$(PREFIX)/lib/
install $(TARGET) $(DESTDIR)/$(PREFIX)/lib/
# The Checker
check:
@echo Files with potentially dangerous functions.
@egrep '[^_.>a-zA-Z0-9](str(n?cpy|n?cat|xfrm|n?dup|str|pbrk|tok|_)\
|stpn?cpy|a?sn?printf|byte_)' $(SOURCES) || true

View File

View File

@ -0,0 +1,39 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr,\
"[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr,\
"[WARN] (%s:%d: errno: %s) " M "\n",\
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) {\
log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\
errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
errno=0; goto error; }
#endif

View File

@ -0,0 +1,45 @@
#include <stdio.h>
#include <ctype.h>
#include "dbg.h"
int print_a_message(const char *msg)
{
printf("A STRING: %s\n", msg);
return 0;
}
int uppercase(const char *msg)
{
int i = 0;
// BUG: \0 termination problems
for(i = 0; msg[i] != '\0'; i++) {
printf("%c", toupper(msg[i]));
}
printf("\n");
return 0;
}
int lowercase(const char *msg)
{
int i = 0;
// BUG: \0 termination problems
for(i = 0; msg[i] != '\0'; i++) {
printf("%c", tolower(msg[i]));
}
printf("\n");
return 0;
}
int fail_on_purpose(const char *msg)
{
return 1;
}

View File

@ -0,0 +1,73 @@
#include "minunit.h"
#include <dlfcn.h>
typedef int (*lib_function) (const char *data);
char *lib_file = "build/libYOUR_LIBRARY.so";
void *lib = NULL;
int check_function(const char *func_to_run, const char *data,
int expected)
{
lib_function func = dlsym(lib, func_to_run);
check(func != NULL,
"Did not find %s function in the library %s: %s", func_to_run,
lib_file, dlerror());
int rc = func(data);
check(rc == expected, "Function %s return %d for data: %s",
func_to_run, rc, data);
return 1;
error:
return 0;
}
char *test_dlopen()
{
lib = dlopen(lib_file, RTLD_NOW);
mu_assert(lib != NULL, "Failed to open the library to test.");
return NULL;
}
char *test_functions()
{
mu_assert(check_function("print_a_message", "Hello", 0),
"print_a_message failed.");
mu_assert(check_function("uppercase", "Hello", 0),
"uppercase failed.");
mu_assert(check_function("lowercase", "Hello", 0),
"lowercase failed.");
return NULL;
}
char *test_failures()
{
mu_assert(check_function("fail_on_purpose", "Hello", 1),
"fail_on_purpose should fail.");
return NULL;
}
char *test_dlclose()
{
int rc = dlclose(lib);
mu_assert(rc == 0, "Failed to close lib.");
return NULL;
}
char *all_tests()
{
mu_suite_start();
mu_run_test(test_dlopen);
mu_run_test(test_functions);
mu_run_test(test_failures);
mu_run_test(test_dlclose);
return NULL;
}
RUN_TESTS(all_tests);

View File

@ -0,0 +1,33 @@
#undef NDEBUG
#ifndef _minunit_h
#define _minunit_h
#include <stdio.h>
#include <dbg.h>
#include <stdlib.h>
#define mu_suite_start() char *message = NULL
#define mu_assert(test, message) if (!(test)) {\
log_err(message); return message; }
#define mu_run_test(test) debug("\n-----%s", " " #test); \
message = test(); tests_run++; if (message) return message;
#define RUN_TESTS(name) int main(int argc, char *argv[]) {\
argc = 1; \
debug("----- RUNNING: %s", argv[0]);\
printf("----\nRUNNING: %s\n", argv[0]);\
char *result = name();\
if (result != 0) {\
printf("FAILED: %s\n", result);\
}\
else {\
printf("ALL TESTS PASSED\n");\
}\
printf("Tests run: %d\n", tests_run);\
exit(result != 0);\
}
int tests_run;
#endif

View File

@ -0,0 +1,19 @@
echo "Running unit tests:"
for i in tests/*_tests
do
if test -f $i
then
if $VALGRIND ./$i 2>> tests/tests.log
then
echo $i PASS
else
echo "ERROR in test $i: here's tests/tests.log"
echo "------"
tail tests/tests.log
exit 1
fi
fi
done
echo ""

8
ex48b/statserve/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
*.o
*.dSYM
*.so
.*.sw*
*.log
build/*
tests/*_tests
bin/statserve

0
ex48b/statserve/LICENSE Normal file
View File

57
ex48b/statserve/Makefile Normal file
View File

@ -0,0 +1,57 @@
LIBS=-llcthw $(OPTLIBS)
LDFLAGS=-L/usr/local/lib $(LIBS)
PREFIX?=/usr/local
SOURCES=$(wildcard src/**/*.c src/*.c)
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
TEST_SRC=$(wildcard tests/*_tests.c)
TESTS=$(patsubst %.c,%,$(TEST_SRC))
TARGET=build/libstatserve.a
SO_TARGET=$(patsubst %.a,%.so,$(TARGET))
# The Target Build
all: $(TARGET) $(SO_TARGET) tests bin/statserve
dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS)
dev: all
$(TARGET): CFLAGS += -fPIC
$(TARGET): build $(OBJECTS)
ar rcs $@ $(OBJECTS)
ranlib $@
$(SO_TARGET): $(TARGET) $(OBJECTS)
$(CC) -shared -o $@ $(LDFLAGS) $(LIBS) $(OBJECTS)
bin/statserve: $(TARGET)
build:
@mkdir -p build
@mkdir -p bin
# The Unit Tests
.PHONY: tests
tests: CFLAGS += $(TARGET)
tests: $(TESTS)
sh ./tests/runtests.sh
# The Cleaner
clean:
rm -rf build $(OBJECTS) $(TESTS)
rm -f tests/tests.log
find . -name "*.gc*" -exec rm {} \;
rm -rf `find . -name "*.dSYM" -print`
# The Install
install: all
install -d $(DESTDIR)/$(PREFIX)/lib/
install $(TARGET) $(DESTDIR)/$(PREFIX)/lib/
# The Checker
check:
@echo Files with potentially dangerous functions.
@egrep '[^_.>a-zA-Z0-9](str(n?cpy|n?cat|xfrm|n?dup|str|pbrk|tok|_)\
|stpn?cpy|a?sn?printf|byte_)' $(SOURCES) || true

View File

View File

@ -0,0 +1,18 @@
#include <statserve.h>
#include <lcthw/dbg.h>
int main(int argc, char *argv[])
{
check(argc == 3, "USAGE: statserve host port");
const char *host = argv[1];
const char *port = argv[2];
check(echo_server(host, port), "Failed to run the echo server.");
return 0;
error:
return 1;
}

39
ex48b/statserve/src/dbg.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr,\
"[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr,\
"[WARN] (%s:%d: errno: %s) " M "\n",\
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) {\
log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\
errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
errno=0; goto error; }
#endif

161
ex48b/statserve/src/net.c Normal file
View File

@ -0,0 +1,161 @@
#include <lcthw/ringbuffer.h>
#include <lcthw/bstrlib.h>
#include <lcthw/dbg.h>
#include <unistd.h>
#include <fcntl.h>
#include "net.h"
struct tagbstring NL = bsStatic("\n");
struct tagbstring CRLF = bsStatic("\r\n");
int nonblock(int fd)
{
int flags = fcntl(fd, F_GETFL, 0);
check(flags >= 0, "Invalid flags on nonblock.");
int rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
check(rc == 0, "Can't set nonblocking.");
return 0;
error:
return -1;
}
int read_some(RingBuffer * buffer, int fd, int is_socket)
{
int rc = 0;
if (RingBuffer_available_data(buffer) == 0) {
buffer->start = buffer->end = 0;
}
if (is_socket) {
rc = recv(fd, RingBuffer_starts_at(buffer),
RingBuffer_available_space(buffer), 0);
} else {
rc = read(fd, RingBuffer_starts_at(buffer),
RingBuffer_available_space(buffer));
}
check(rc >= 0, "Failed to read from fd: %d", fd);
RingBuffer_commit_write(buffer, rc);
return rc;
error:
return -1;
}
int write_some(RingBuffer * buffer, int fd, int is_socket)
{
int rc = 0;
bstring data = RingBuffer_get_all(buffer);
check(data != NULL, "Failed to get from the buffer.");
check(bfindreplace(data, &NL, &CRLF, 0) == BSTR_OK,
"Failed to replace NL.");
if (is_socket) {
rc = send(fd, bdata(data), blength(data), 0);
} else {
rc = write(fd, bdata(data), blength(data));
}
check(rc == blength(data), "Failed to write everything to fd: %d.",
fd);
bdestroy(data);
return rc;
error:
return -1;
}
int attempt_listen(struct addrinfo *info)
{
int sockfd = 0;
int rc = -1;
int yes = 1;
sockfd = socket(info->ai_family, info->ai_socktype,
info->ai_protocol);
check_debug(sockfd != -1, "Failed to bind to address result. Trying more.");
rc = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
check_debug(rc == 0, "Failed to set SO_REISEADDR.");
rc = bind(sockfd, info->ai_addr, info->ai_addrlen);
check_debug(rc == 0, "Failed to bind socket.");
rc = listen(sockfd, BACKLOG);
check_debug(rc == 0, "Failed to listen to socket.");
return sockfd;
error:
return -1;
}
int server_listen(const char *host, const char *port)
{
int rc = 0;
int sockfd = -1;
struct addrinfo *info = NULL;
struct addrinfo *next_p = NULL;
struct addrinfo addr = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_flags = AI_PASSIVE
};
check(host != NULL, "Must give a valid host.");
check(port != NULL, "Must have a valid port.");
rc = getaddrinfo(NULL, port, &addr, &info);
check(rc == 0, "Failed to get address info for connect.");
for(next_p = info; next_p != NULL; next_p = next_p->ai_next)
{
sockfd = attempt_listen(next_p);
if(sockfd != -1) break;
}
check(sockfd != -1, "All possible addresses failed.");
if(info) freeaddrinfo(info);
return sockfd;
error: // fallthrough
if(info) freeaddrinfo(info);
return -1;
}
int client_connect(char *host, char *port)
{
int rc = 0;
struct addrinfo *addr = NULL;
rc = getaddrinfo(host, port, NULL, &addr);
check(rc == 0, "Failed to lookup %s:%s", host, port);
int sock = socket(AF_INET, SOCK_STREAM, 0);
check(sock >= 0, "Cannot create a socket.");
rc = connect(sock, addr->ai_addr, addr->ai_addrlen);
check(rc == 0, "Connect failed.");
rc = nonblock(sock);
check(rc == 0, "Can't set nonblocking.");
freeaddrinfo(addr);
return sock;
error:
freeaddrinfo(addr);
return -1;
}

18
ex48b/statserve/src/net.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef _statserve_net_h
#define _statserve_net_h
#include <netdb.h>
#define BACKLOG 10
int nonblock(int fd);
int read_some(RingBuffer * buffer, int fd, int is_socket);
int write_some(RingBuffer * buffer, int fd, int is_socket);
int attempt_listen(struct addrinfo *info);
int server_listen(const char *host, const char *port);
#endif

View File

@ -0,0 +1,84 @@
#include <lcthw/ringbuffer.h>
#include <lcthw/dbg.h>
#include <unistd.h>
#include <stdlib.h>
#include "statserve.h"
#include "net.h"
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>
const int RB_SIZE = 1024 * 10;
int client_handler(int fd)
{
int rc = 0;
RingBuffer *sock_rb = RingBuffer_create(RB_SIZE);
// child process
while(read_some(sock_rb, fd, 1) != -1) {
if(write_some(sock_rb, fd, 1) == -1) {
log_info("Client closed.");
break;
}
}
rc = close(fd);
check(rc != -1, "Failed to close fd.");
error: // fallthrough
if(sock_rb) RingBuffer_destroy(sock_rb);
exit(0);
}
void handle_sigchld(int sig) {
while(waitpid(-1, NULL, WNOHANG) > 0) {
}
}
int echo_server(const char *host, const char *port)
{
struct sockaddr_in client_addr;
socklen_t sin_size = sizeof(client_addr);
int server_socket = 0;
int client_fd = 0;
int rc = 0;
struct sigaction sa = {
.sa_handler = handle_sigchld,
.sa_flags = SA_RESTART | SA_NOCLDSTOP
};
sigemptyset(&sa.sa_mask);
rc = sigaction(SIGCHLD, &sa, 0);
check(rc != -1, "Failed to setup signal handler for child processes.");
server_socket = server_listen(host, port);
check(server_socket >= 0, "bind to %s:%s failed.",
host, port);
while (1) {
client_fd = accept(server_socket, (struct sockaddr *)&client_addr, &sin_size);
check(client_fd >= 0, "Failed to accept connection.");
log_info("Client connected.");
rc = fork();
check(rc != -1, "Failed to fork!");
if(rc == 0) {
// in the child process
close(server_socket);
client_handler(client_fd);
} else {
// server process
close(client_fd);
}
}
return 0;
error:
return -1;
}

View File

@ -0,0 +1,7 @@
#ifndef _statserve_h
#define _statserve_h
int echo_server(const char *host, const char *port);
#endif

View File

@ -0,0 +1,33 @@
#undef NDEBUG
#ifndef _minunit_h
#define _minunit_h
#include <stdio.h>
#include <dbg.h>
#include <stdlib.h>
#define mu_suite_start() char *message = NULL
#define mu_assert(test, message) if (!(test)) {\
log_err(message); return message; }
#define mu_run_test(test) debug("\n-----%s", " " #test); \
message = test(); tests_run++; if (message) return message;
#define RUN_TESTS(name) int main(int argc, char *argv[]) {\
argc = 1; \
debug("----- RUNNING: %s", argv[0]);\
printf("----\nRUNNING: %s\n", argv[0]);\
char *result = name();\
if (result != 0) {\
printf("FAILED: %s\n", result);\
}\
else {\
printf("ALL TESTS PASSED\n");\
}\
printf("Tests run: %d\n", tests_run);\
exit(result != 0);\
}
int tests_run;
#endif

View File

@ -0,0 +1,19 @@
echo "Running unit tests:"
for i in tests/*_tests
do
if test -f $i
then
if $VALGRIND ./$i 2>> tests/tests.log
then
echo $i PASS
else
echo "ERROR in test $i: here's tests/tests.log"
echo "------"
tail tests/tests.log
exit 1
fi
fi
done
echo ""

View File

@ -0,0 +1,23 @@
#include "minunit.h"
#include <statserve.h>
char *test_statserve()
{
// mu_assert(echo_server("127.0.0.1", "7899") == 0, "Failed to start echo server.");
return NULL;
}
char *all_tests()
{
mu_suite_start();
mu_run_test(test_statserve);
return NULL;
}
RUN_TESTS(all_tests);

8
ex49b/statserve/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
*.o
*.dSYM
*.so
.*.sw*
*.log
build/*
tests/*_tests
bin/statserve

0
ex49b/statserve/LICENSE Normal file
View File

58
ex49b/statserve/Makefile Normal file
View File

@ -0,0 +1,58 @@
CFLAGS=-g -O2 -Wall -Wextra -Isrc -I/usr/local/include -rdynamic $(OPTFLAGS)
LIBS=-llcthw $(OPTLIBS)
LDFLAGS=-L/usr/local/lib $(LIBS)
PREFIX?=/usr/local
SOURCES=$(wildcard src/**/*.c src/*.c)
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
TEST_SRC=$(wildcard tests/*_tests.c)
TESTS=$(patsubst %.c,%,$(TEST_SRC))
TARGET=build/libstatserve.a
SO_TARGET=$(patsubst %.a,%.so,$(TARGET))
# The Target Build
all: $(TARGET) $(SO_TARGET) tests bin/statserve
dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS)
dev: all
$(TARGET): CFLAGS += -fPIC
$(TARGET): build $(OBJECTS)
ar rcs $@ $(OBJECTS)
ranlib $@
$(SO_TARGET): $(TARGET) $(OBJECTS)
$(CC) -shared -o $@ $(LDFLAGS) $(LIBS) $(OBJECTS)
bin/statserve: $(TARGET)
build:
@mkdir -p build
@mkdir -p bin
# The Unit Tests
.PHONY: tests
tests: CFLAGS += $(TARGET)
tests: $(TESTS)
sh ./tests/runtests.sh
# The Cleaner
clean:
rm -rf build $(OBJECTS) $(TESTS)
rm -f tests/tests.log
find . -name "*.gc*" -exec rm {} \;
rm -rf `find . -name "*.dSYM" -print`
# The Install
install: all
install -d $(DESTDIR)/$(PREFIX)/lib/
install $(TARGET) $(DESTDIR)/$(PREFIX)/lib/
# The Checker
check:
@echo Files with potentially dangerous functions.
@egrep '[^_.>a-zA-Z0-9](str(n?cpy|n?cat|xfrm|n?dup|str|pbrk|tok|_)\
|stpn?cpy|a?sn?printf|byte_)' $(SOURCES) || true

View File

View File

@ -0,0 +1,18 @@
#include <statserve.h>
#include <lcthw/dbg.h>
int main(int argc, char *argv[])
{
check(argc == 3, "USAGE: statserve host port");
const char *host = argv[1];
const char *port = argv[2];
check(echo_server(host, port), "Failed to run the echo server.");
return 0;
error:
return 1;
}

39
ex49b/statserve/src/dbg.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr,\
"[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr,\
"[WARN] (%s:%d: errno: %s) " M "\n",\
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) {\
log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\
errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
errno=0; goto error; }
#endif

184
ex49b/statserve/src/net.c Normal file
View File

@ -0,0 +1,184 @@
#include <lcthw/ringbuffer.h>
#include <lcthw/bstrlib.h>
#include <lcthw/dbg.h>
#include <unistd.h>
#include <fcntl.h>
#include "net.h"
struct tagbstring NL = bsStatic("\n");
struct tagbstring CRLF = bsStatic("\r\n");
int nonblock(int fd)
{
int flags = fcntl(fd, F_GETFL, 0);
check(flags >= 0, "Invalid flags on nonblock.");
int rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
check(rc == 0, "Can't set nonblocking.");
return 0;
error:
return -1;
}
int read_some(RingBuffer * buffer, int fd, int is_socket)
{
int rc = 0;
if (RingBuffer_available_data(buffer) == 0) {
buffer->start = buffer->end = 0;
}
if (is_socket) {
rc = recv(fd, RingBuffer_starts_at(buffer),
RingBuffer_available_space(buffer), 0);
} else {
rc = read(fd, RingBuffer_starts_at(buffer),
RingBuffer_available_space(buffer));
}
check(rc >= 0, "Failed to read from fd: %d", fd);
RingBuffer_commit_write(buffer, rc);
return rc;
error:
return -1;
}
int write_some(RingBuffer * buffer, int fd, int is_socket)
{
int rc = 0;
bstring data = RingBuffer_get_all(buffer);
check(data != NULL, "Failed to get from the buffer.");
check(bfindreplace(data, &NL, &CRLF, 0) == BSTR_OK,
"Failed to replace NL.");
if (is_socket) {
rc = send(fd, bdata(data), blength(data), 0);
} else {
rc = write(fd, bdata(data), blength(data));
}
check(rc == blength(data), "Failed to write everything to fd: %d.",
fd);
bdestroy(data);
return rc;
error:
return -1;
}
int attempt_listen(struct addrinfo *info)
{
int sockfd = 0;
int rc = -1;
int yes = 1;
sockfd = socket(info->ai_family, info->ai_socktype,
info->ai_protocol);
check_debug(sockfd != -1, "Failed to bind to address result. Trying more.");
rc = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
check_debug(rc == 0, "Failed to set SO_REISEADDR.");
rc = bind(sockfd, info->ai_addr, info->ai_addrlen);
check_debug(rc == 0, "Failed to bind socket.");
rc = listen(sockfd, BACKLOG);
check_debug(rc == 0, "Failed to listen to socket.");
return sockfd;
error:
return -1;
}
int server_listen(const char *host, const char *port)
{
int rc = 0;
int sockfd = -1;
struct addrinfo *info = NULL;
struct addrinfo *next_p = NULL;
struct addrinfo addr = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_flags = AI_PASSIVE
};
check(host != NULL, "Must give a valid host.");
check(port != NULL, "Must have a valid port.");
rc = getaddrinfo(NULL, port, &addr, &info);
check(rc == 0, "Failed to get address info for connect.");
for(next_p = info; next_p != NULL; next_p = next_p->ai_next)
{
sockfd = attempt_listen(next_p);
if(sockfd != -1) break;
}
check(sockfd != -1, "All possible addresses failed.");
if(info) freeaddrinfo(info);
return sockfd;
error: // fallthrough
if(info) freeaddrinfo(info);
return -1;
}
int client_connect(char *host, char *port)
{
int rc = 0;
struct addrinfo *addr = NULL;
rc = getaddrinfo(host, port, NULL, &addr);
check(rc == 0, "Failed to lookup %s:%s", host, port);
int sock = socket(AF_INET, SOCK_STREAM, 0);
check(sock >= 0, "Cannot create a socket.");
rc = connect(sock, addr->ai_addr, addr->ai_addrlen);
check(rc == 0, "Connect failed.");
rc = nonblock(sock);
check(rc == 0, "Can't set nonblocking.");
freeaddrinfo(addr);
return sock;
error:
freeaddrinfo(addr);
return -1;
}
bstring read_line(RingBuffer *input, const char line_ending)
{
int i = 0;
bstring result = NULL;
for(i = 0; i < RingBuffer_available_data(input); i++) {
if(input->buffer[i] == line_ending) {
result = RingBuffer_gets(input, i);
check(result, "Failed to get line from RingBuffer.");
check(RingBuffer_available_data(input) >= 1, "Not enough data in the RingBuffer after reading a line.");
// eat the \n in the buffer
RingBuffer_commit_read(input, 1);
break;
}
}
debug("LINE: %s", bdata(result));
return result;
error:
return NULL;
}

20
ex49b/statserve/src/net.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef _statserve_net_h
#define _statserve_net_h
#include <netdb.h>
#define BACKLOG 10
int nonblock(int fd);
int read_some(RingBuffer * buffer, int fd, int is_socket);
int write_some(RingBuffer * buffer, int fd, int is_socket);
int attempt_listen(struct addrinfo *info);
int server_listen(const char *host, const char *port);
bstring read_line(RingBuffer *input, const char line_ending);
#endif

View File

@ -0,0 +1,320 @@
#include <lcthw/ringbuffer.h>
#include <lcthw/dbg.h>
#include <lcthw/hashmap.h>
#include <lcthw/stats.h>
#include <unistd.h>
#include <stdlib.h>
#include "statserve.h"
#include "net.h"
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/file.h>
const int RB_SIZE = 1024 * 10;
struct tagbstring LINE_SPLIT = bsStatic(" ");
struct tagbstring CREATE = bsStatic("create");
struct tagbstring STDDEV = bsStatic("stddev");
struct tagbstring MEAN = bsStatic("mean");
struct tagbstring SAMPLE = bsStatic("sample");
struct tagbstring DUMP = bsStatic("dump");
struct tagbstring DELETE = bsStatic("delete");
struct tagbstring OK = bsStatic("OK\n");
struct tagbstring ERR = bsStatic("ERR\n");
struct tagbstring DNE = bsStatic("DNE\n");
struct tagbstring EXISTS = bsStatic("EXISTS\n");
const char LINE_ENDING = '\n';
// this is just temporary to work out the protocol
// it actually doesn't work in practice because forking
Hashmap *DATA = NULL;
struct Command;
typedef int (*handler_cb)(struct Command *cmd, RingBuffer *send_rb);
typedef struct Command {
bstring command;
bstring name;
bstring number;
handler_cb handler;
} Command;
typedef struct Record {
bstring name;
Stats *stat;
} Record;
void send_reply(RingBuffer *send_rb, bstring reply)
{
RingBuffer_puts(send_rb, reply);
}
int handle_create(Command *cmd, RingBuffer *send_rb)
{
if(Hashmap_get(DATA, cmd->name)) {
send_reply(send_rb, &EXISTS);
} else {
log_info("create: %s %s", bdata(cmd->name), bdata(cmd->number));
Record *info = calloc(sizeof(Record), 1);
check_mem(info);
info->stat = Stats_create();
check_mem(info->stat);
info->name = bstrcpy(cmd->name);
check_mem(info->name);
Stats_sample(info->stat, atof(bdata(cmd->number)));
Hashmap_set(DATA, info->name, info);
send_reply(send_rb, &OK);
}
return 0;
error:
return -1;
}
int handle_mean(Command *cmd, RingBuffer *send_rb)
{
log_info("mean: %s", bdata(cmd->name));
Record *info = Hashmap_get(DATA, cmd->name);
if(info == NULL) {
send_reply(send_rb, &DNE);
} else {
bstring reply = bformat("%f\n", Stats_mean(info->stat));
send_reply(send_rb, reply);
bdestroy(reply);
}
return 0;
}
int handle_sample(Command *cmd, RingBuffer *send_rb)
{
log_info("sample: %s %s", bdata(cmd->name), bdata(cmd->number));
Record *info = Hashmap_get(DATA, cmd->name);
if(info == NULL) {
send_reply(send_rb, &DNE);
} else {
Stats_sample(info->stat, atof(bdata(cmd->number)));
bstring reply = bformat("%f\n", Stats_mean(info->stat));
send_reply(send_rb, reply);
bdestroy(reply);
}
return 0;
}
int handle_dump(Command *cmd, RingBuffer *send_rb)
{
log_info("dump: %s", bdata(cmd->name));
Record *info = Hashmap_get(DATA, cmd->name);
if(info == NULL) {
send_reply(send_rb, &DNE);
} else {
bstring reply = bformat("%f %f %f %f %ld %f %f\n",
Stats_mean(info->stat),
Stats_stddev(info->stat),
info->stat->sum,
info->stat->sumsq,
info->stat->n,
info->stat->min,
info->stat->max);
send_reply(send_rb, reply);
bdestroy(reply);
}
return 0;
}
int handle_delete(Command *cmd, RingBuffer *send_rb)
{
log_info("delete: %s", bdata(cmd->name));
Record *info = Hashmap_get(DATA, cmd->name);
if(info == NULL) {
send_reply(send_rb, &DNE);
} else {
free(info->stat);
bdestroy(info->name);
free(info);
Hashmap_delete(DATA, cmd->name);
send_reply(send_rb, &OK);
}
return 0;
}
int handle_stddev(Command *cmd, RingBuffer *send_rb)
{
log_info("stddev: %s", bdata(cmd->name));
Record *info = Hashmap_get(DATA, cmd->name);
if(info == NULL) {
send_reply(send_rb, &DNE);
} else {
bstring reply = bformat("%f\n", Stats_stddev(info->stat));
send_reply(send_rb, reply);
bdestroy(reply);
}
return 0;
}
int parse_command(struct bstrList *splits, Command *cmd)
{
cmd->command = splits->entry[0];
if(biseq(cmd->command, &CREATE)) {
check(splits->qty == 3, "Failed to parse create: %d", splits->qty);
cmd->name = splits->entry[1];
cmd->number = splits->entry[2];
cmd->handler = handle_create;
} else if(biseq(cmd->command, &MEAN)) {
check(splits->qty == 2, "Failed to parse mean: %d", splits->qty);
cmd->name = splits->entry[1];
cmd->handler = handle_mean;
} else if(biseq(cmd->command, &SAMPLE)) {
check(splits->qty == 3, "Failed to parse sample: %d", splits->qty);
cmd->name = splits->entry[1];
cmd->number = splits->entry[2];
cmd->handler = handle_sample;
} else if(biseq(cmd->command, &DUMP)) {
check(splits->qty == 2, "Failed to parse dump: %d", splits->qty);
cmd->name = splits->entry[1];
cmd->handler = handle_dump;
} else if(biseq(cmd->command, &DELETE)) {
check(splits->qty == 2, "Failed to parse delete: %d", splits->qty);
cmd->name = splits->entry[1];
cmd->handler = handle_delete;
} else if(biseq(cmd->command, &STDDEV)) {
check(splits->qty == 2, "Failed to parse stddev: %d", splits->qty);
cmd->name = splits->entry[1];
cmd->handler = handle_stddev;
} else {
sentinel("Failed to parse the command.");
}
return 0;
error:
return 1;
}
int parse_line(bstring data, RingBuffer *send_rb)
{
int rc = 0;
Command cmd = {.command = NULL};
struct bstrList *splits = bsplits(data, &LINE_SPLIT);
check(splits != NULL, "Bad data.");
rc = parse_command(splits, &cmd);
check(rc == 0, "Failed to parse command.");
rc = cmd.handler(&cmd, send_rb);
bstrListDestroy(splits);
return rc;
error:
if(splits) bstrListDestroy(splits);
return -1;
}
int client_handler(int fd)
{
int rc = 0;
RingBuffer *recv_rb = RingBuffer_create(RB_SIZE);
RingBuffer *send_rb = RingBuffer_create(RB_SIZE);
check_mem(recv_rb);
check_mem(send_rb);
while(read_some(recv_rb, fd, 1) != -1) {
// TODO: read a line, put the rest back
bstring data = read_line(recv_rb, LINE_ENDING);
check(data != NULL, "Client closed.");
rc = parse_line(data, send_rb);
check(rc == 0, "Failed to parse user. Closing.");
if(RingBuffer_available_data(send_rb)) {
write_some(send_rb, fd, 1);
}
bdestroy(data);
}
rc = close(fd);
check(rc != -1, "Failed to close fd.");
error: // fallthrough
if(recv_rb) RingBuffer_destroy(recv_rb);
exit(0);
}
void handle_sigchld(int sig) {
if(sig == SIGCHLD) {
while(waitpid(-1, NULL, WNOHANG) > 0) {
}
}
}
int echo_server(const char *host, const char *port)
{
struct sockaddr_in client_addr;
socklen_t sin_size = sizeof(client_addr);
int server_socket = 0;
int client_fd = 0;
int rc = 0;
DATA = Hashmap_create(NULL, NULL);
check_mem(DATA);
struct sigaction sa = {
.sa_handler = handle_sigchld,
.sa_flags = SA_RESTART | SA_NOCLDSTOP
};
sigemptyset(&sa.sa_mask);
rc = sigaction(SIGCHLD, &sa, 0);
check(rc != -1, "Failed to setup signal handler for child processes.");
server_socket = server_listen(host, port);
check(server_socket >= 0, "bind to %s:%s failed.",
host, port);
while (1) {
client_fd = accept(server_socket, (struct sockaddr *)&client_addr, &sin_size);
check(client_fd >= 0, "Failed to accept connection.");
log_info("Client connected.");
rc = fork();
check(rc != -1, "Failed to fork!");
if(rc == 0) {
// in the child process
close(server_socket);
client_handler(client_fd);
} else {
// server process doesn't need this
close(client_fd);
}
}
return 0;
error:
return -1;
}

View File

@ -0,0 +1,7 @@
#ifndef _statserve_h
#define _statserve_h
int echo_server(const char *host, const char *port);
#endif

View File

@ -0,0 +1,33 @@
#undef NDEBUG
#ifndef _minunit_h
#define _minunit_h
#include <stdio.h>
#include <dbg.h>
#include <stdlib.h>
#define mu_suite_start() char *message = NULL
#define mu_assert(test, message) if (!(test)) {\
log_err(message); return message; }
#define mu_run_test(test) debug("\n-----%s", " " #test); \
message = test(); tests_run++; if (message) return message;
#define RUN_TESTS(name) int main(int argc, char *argv[]) {\
argc = 1; \
debug("----- RUNNING: %s", argv[0]);\
printf("----\nRUNNING: %s\n", argv[0]);\
char *result = name();\
if (result != 0) {\
printf("FAILED: %s\n", result);\
}\
else {\
printf("ALL TESTS PASSED\n");\
}\
printf("Tests run: %d\n", tests_run);\
exit(result != 0);\
}
int tests_run;
#endif

View File

@ -0,0 +1,19 @@
echo "Running unit tests:"
for i in tests/*_tests
do
if test -f $i
then
if $VALGRIND ./$i 2>> tests/tests.log
then
echo $i PASS
else
echo "ERROR in test $i: here's tests/tests.log"
echo "------"
tail tests/tests.log
exit 1
fi
fi
done
echo ""

View File

@ -0,0 +1,23 @@
#include "minunit.h"
#include <statserve.h>
char *test_statserve()
{
// mu_assert(echo_server("127.0.0.1", "7899") == 0, "Failed to start echo server.");
return NULL;
}
char *all_tests()
{
mu_suite_start();
mu_run_test(test_statserve);
return NULL;
}
RUN_TESTS(all_tests);

7
ex50b/statserve/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
*.o
*.dSYM
*.so
.*.sw*
*.log
build/*
tests/*_tests

0
ex50b/statserve/LICENSE Normal file
View File

58
ex50b/statserve/Makefile Normal file
View File

@ -0,0 +1,58 @@
CFLAGS=-g -O2 -Wall -Wextra -Isrc -I/usr/local/include -rdynamic $(OPTFLAGS)
LIBS=-llcthw $(OPTLIBS)
LDFLAGS=-L/usr/local/lib $(LIBS)
PREFIX?=/usr/local
SOURCES=$(wildcard src/**/*.c src/*.c)
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
TEST_SRC=$(wildcard tests/*_tests.c)
TESTS=$(patsubst %.c,%,$(TEST_SRC))
TARGET=build/libstatserve.a
SO_TARGET=$(patsubst %.a,%.so,$(TARGET))
# The Target Build
all: $(TARGET) $(SO_TARGET) tests bin/statserve
dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS)
dev: all
$(TARGET): CFLAGS += -fPIC
$(TARGET): build $(OBJECTS)
ar rcs $@ $(OBJECTS)
ranlib $@
$(SO_TARGET): $(TARGET) $(OBJECTS)
$(CC) -shared -o $@ $(LDFLAGS) $(LIBS) $(OBJECTS)
bin/statserve: $(TARGET)
build:
@mkdir -p build
@mkdir -p bin
# The Unit Tests
.PHONY: tests
tests: CFLAGS += $(TARGET)
tests: $(TESTS)
sh ./tests/runtests.sh
# The Cleaner
clean:
rm -rf build $(OBJECTS) $(TESTS)
rm -f tests/tests.log
find . -name "*.gc*" -exec rm {} \;
rm -rf `find . -name "*.dSYM" -print`
# The Install
install: all
install -d $(DESTDIR)/$(PREFIX)/lib/
install $(TARGET) $(DESTDIR)/$(PREFIX)/lib/
# The Checker
check:
@echo Files with potentially dangerous functions.
@egrep '[^_.>a-zA-Z0-9](str(n?cpy|n?cat|xfrm|n?dup|str|pbrk|tok|_)\
|stpn?cpy|a?sn?printf|byte_)' $(SOURCES) || true

View File

View File

@ -0,0 +1,18 @@
#include <statserve.h>
#include <lcthw/dbg.h>
int main(int argc, char *argv[])
{
check(argc == 3, "USAGE: statserve host port");
const char *host = argv[1];
const char *port = argv[2];
check(run_server(host, port), "Failed to run the echo server.");
return 0;
error:
return 1;
}

39
ex50b/statserve/src/dbg.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr,\
"[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__,\
clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr,\
"[WARN] (%s:%d: errno: %s) " M "\n",\
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) {\
log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__);\
errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__);\
errno=0; goto error; }
#endif

191
ex50b/statserve/src/net.c Normal file
View File

@ -0,0 +1,191 @@
#include <lcthw/ringbuffer.h>
#include <lcthw/bstrlib.h>
#include <lcthw/dbg.h>
#include <unistd.h>
#include <fcntl.h>
#include "net.h"
struct tagbstring NL = bsStatic("\n");
struct tagbstring CRLF = bsStatic("\r\n");
int nonblock(int fd)
{
int flags = fcntl(fd, F_GETFL, 0);
check(flags >= 0, "Invalid flags on nonblock.");
int rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
check(rc == 0, "Can't set nonblocking.");
return 0;
error:
return -1;
}
int read_some(RingBuffer * buffer, int fd, int is_socket)
{
int rc = 0;
if (RingBuffer_available_data(buffer) == 0) {
buffer->start = buffer->end = 0;
}
if (is_socket) {
rc = recv(fd, RingBuffer_starts_at(buffer),
RingBuffer_available_space(buffer), 0);
} else {
rc = read(fd, RingBuffer_starts_at(buffer),
RingBuffer_available_space(buffer));
}
check(rc >= 0, "Failed to read from fd: %d", fd);
RingBuffer_commit_write(buffer, rc);
return rc;
error:
return -1;
}
int write_some(RingBuffer * buffer, int fd, int is_socket)
{
int rc = 0;
bstring data = RingBuffer_get_all(buffer);
check(data != NULL, "Failed to get from the buffer.");
check(bfindreplace(data, &NL, &CRLF, 0) == BSTR_OK,
"Failed to replace NL.");
if (is_socket) {
rc = send(fd, bdata(data), blength(data), 0);
} else {
rc = write(fd, bdata(data), blength(data));
}
check(rc == blength(data), "Failed to write everything to fd: %d.",
fd);
bdestroy(data);
return rc;
error:
return -1;
}
int attempt_listen(struct addrinfo *info)
{
int sockfd = 0;
int rc = -1;
int yes = 1;
sockfd = socket(info->ai_family, info->ai_socktype,
info->ai_protocol);
check_debug(sockfd != -1, "Failed to bind to address result. Trying more.");
rc = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
check_debug(rc == 0, "Failed to set SO_REISEADDR.");
rc = bind(sockfd, info->ai_addr, info->ai_addrlen);
check_debug(rc == 0, "Failed to bind socket.");
rc = listen(sockfd, BACKLOG);
check_debug(rc == 0, "Failed to listen to socket.");
return sockfd;
error:
return -1;
}
int server_listen(const char *host, const char *port)
{
int rc = 0;
int sockfd = -1;
struct addrinfo *info = NULL;
struct addrinfo *next_p = NULL;
struct addrinfo addr = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_flags = AI_PASSIVE
};
check(host != NULL, "Must give a valid host.");
check(port != NULL, "Must have a valid port.");
rc = getaddrinfo(NULL, port, &addr, &info);
check(rc == 0, "Failed to get address info for connect.");
for(next_p = info; next_p != NULL; next_p = next_p->ai_next)
{
sockfd = attempt_listen(next_p);
if(sockfd != -1) break;
}
check(sockfd != -1, "All possible addresses failed.");
if(info) freeaddrinfo(info);
return sockfd;
error: // fallthrough
if(info) freeaddrinfo(info);
return -1;
}
int client_connect(char *host, char *port)
{
int rc = 0;
struct addrinfo *addr = NULL;
rc = getaddrinfo(host, port, NULL, &addr);
check(rc == 0, "Failed to lookup %s:%s", host, port);
int sock = socket(AF_INET, SOCK_STREAM, 0);
check(sock >= 0, "Cannot create a socket.");
rc = connect(sock, addr->ai_addr, addr->ai_addrlen);
check(rc == 0, "Connect failed.");
rc = nonblock(sock);
check(rc == 0, "Can't set nonblocking.");
freeaddrinfo(addr);
return sock;
error:
freeaddrinfo(addr);
return -1;
}
bstring read_line(RingBuffer *input, const char line_ending)
{
int i = 0;
bstring result = NULL;
for(i = 0; i < RingBuffer_available_data(input); i++) {
if(input->buffer[i] == line_ending) {
result = RingBuffer_gets(input, i);
check(result, "Failed to get line from RingBuffer.");
check(RingBuffer_available_data(input) >= 1, "Not enough data in the RingBuffer after reading a line.");
// eat the \n in the buffer
RingBuffer_commit_read(input, 1);
break;
}
}
debug("LINE: %s", bdata(result));
return result;
error:
return NULL;
}
void send_reply(RingBuffer *send_rb, bstring reply)
{
RingBuffer_puts(send_rb, reply);
}

23
ex50b/statserve/src/net.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef _statserve_net_h
#define _statserve_net_h
#include <netdb.h>
#include <lcthw/ringbuffer.h>
#define BACKLOG 10
int nonblock(int fd);
int read_some(RingBuffer * buffer, int fd, int is_socket);
int write_some(RingBuffer * buffer, int fd, int is_socket);
int attempt_listen(struct addrinfo *info);
int server_listen(const char *host, const char *port);
bstring read_line(RingBuffer *input, const char line_ending);
void send_reply(RingBuffer *send_rb, bstring reply);
#endif

View File

@ -0,0 +1,382 @@
#include <lcthw/ringbuffer.h>
#include <lcthw/dbg.h>
#include <lcthw/hashmap.h>
#include <lcthw/stats.h>
#include <unistd.h>
#include <stdlib.h>
#include "statserve.h"
#include "net.h"
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/file.h>
const int RB_SIZE = 1024 * 10;
struct tagbstring LINE_SPLIT = bsStatic(" ");
struct tagbstring CREATE = bsStatic("create");
struct tagbstring STDDEV = bsStatic("stddev");
struct tagbstring MEAN = bsStatic("mean");
struct tagbstring SAMPLE = bsStatic("sample");
struct tagbstring DUMP = bsStatic("dump");
struct tagbstring DELETE = bsStatic("delete");
struct tagbstring OK = bsStatic("OK\n");
struct tagbstring ERR = bsStatic("ERR\n");
struct tagbstring DNE = bsStatic("DNE\n");
struct tagbstring EXISTS = bsStatic("EXISTS\n");
struct tagbstring SLASH = bsStatic("/");
const char LINE_ENDING = '\n';
// this is just temporary to work out the protocol
// it actually doesn't work in practice because forking
Hashmap *DATA = NULL;
struct Command;
typedef int (*handler_cb)(struct Command *cmd, RingBuffer *send_rb, bstring path);
typedef struct Command {
bstring command;
bstring name;
struct bstrList *path;
bstring number;
handler_cb handler;
} Command;
typedef struct Record {
bstring name;
Stats *stat;
} Record;
int handle_create(Command *cmd, RingBuffer *send_rb, bstring path)
{
Record *info = Hashmap_get(DATA, path);
// BUG: does duplicates of children
if(info != NULL && biseq(path, cmd->name)) {
// report the root exists, don't report children
send_reply(send_rb, &EXISTS);
} else if(info != NULL) {
// skip children and don't overwrite them
debug("Child exists so skipping it.");
return 0;
} else {
// brand spanking new so make it
debug("create: %s %s", bdata(path), bdata(cmd->number));
Record *info = calloc(sizeof(Record), 1);
check_mem(info);
info->stat = Stats_create();
check_mem(info->stat);
info->name = bstrcpy(path);
check_mem(info->name);
Stats_sample(info->stat, atof(bdata(cmd->number)));
Hashmap_set(DATA, info->name, info);
// only send the OK on the last path part
if(cmd->path->qty == 2) {
send_reply(send_rb, &OK);
}
}
return 0;
error:
return -1;
}
int handle_mean(Command *cmd, RingBuffer *send_rb, bstring path)
{
debug("mean: %s from %s", bdata(path), bdata(cmd->name));
Record *info = Hashmap_get(DATA, path);
if(info == NULL) {
send_reply(send_rb, &DNE);
} else {
bstring reply = bformat("%f\n", Stats_mean(info->stat));
send_reply(send_rb, reply);
bdestroy(reply);
}
return 0;
}
int handle_sample(Command *cmd, RingBuffer *send_rb, bstring path)
{
debug("sample: %s %s", bdata(path), bdata(cmd->number));
Record *info = Hashmap_get(DATA, path);
bstring child_path = NULL;
if(info == NULL) {
send_reply(send_rb, &DNE);
} else {
debug("found info: '%s' vs. '%s'", bdata(path), bdata(cmd->name));
if(biseq(path, cmd->name)) {
Stats_sample(info->stat, atof(bdata(cmd->number)));
} else {
// need to do a bit of hackery to get the child path
// then we can do a mean-of-means on it
cmd->path->qty++;
child_path = bjoin(cmd->path, &SLASH);
debug("child_path: %s", bdata(child_path));
Record *child_info = Hashmap_get(DATA, child_path);
if(child_info) {
Stats_sample(info->stat, Stats_mean(child_info->stat));
}
cmd->path->qty--; // drop it back down to continue
}
}
bstring reply = bformat("%f\n", Stats_mean(info->stat));
send_reply(send_rb, reply);
bdestroy(reply);
return 0;
}
int handle_dump(Command *cmd, RingBuffer *send_rb, bstring path)
{
debug("dump: %s from %s", bdata(path), bdata(cmd->name));
Record *info = Hashmap_get(DATA, path);
if(info == NULL) {
send_reply(send_rb, &DNE);
} else {
bstring reply = bformat("%s %f %f %f %f %ld %f %f\n",
bdata(info->name),
Stats_mean(info->stat),
Stats_stddev(info->stat),
info->stat->sum,
info->stat->sumsq,
info->stat->n,
info->stat->min,
info->stat->max);
send_reply(send_rb, reply);
bdestroy(reply);
}
return 0;
}
int handle_delete(Command *cmd, RingBuffer *send_rb, bstring path)
{
debug("delete: %s from %s", bdata(path), bdata(cmd->name));
Record *info = Hashmap_get(DATA, path);
if(info == NULL) {
send_reply(send_rb, &DNE);
} else {
free(info->stat);
bdestroy(info->name);
free(info);
Hashmap_delete(DATA, path);
send_reply(send_rb, &OK);
}
return 0;
}
int handle_stddev(Command *cmd, RingBuffer *send_rb, bstring path)
{
debug("stddev: %s from %s", bdata(path), bdata(cmd->name));
Record *info = Hashmap_get(DATA, path);
if(info == NULL) {
send_reply(send_rb, &DNE);
} else {
bstring reply = bformat("%f\n", Stats_stddev(info->stat));
send_reply(send_rb, reply);
bdestroy(reply);
}
return 0;
}
int scan_paths(Command *cmd, RingBuffer *send_rb)
{
size_t qty = cmd->path->qty;
int rc = 0;
for(;cmd->path->qty > 1; cmd->path->qty--) {
bstring path = bjoin(cmd->path, &SLASH);
rc = cmd->handler(cmd, send_rb, path);
bdestroy(path);
if(rc != 0) break;
}
cmd->path->qty = qty;
return rc;
}
struct bstrList *parse_name(bstring name)
{
return bsplits(name, &SLASH);
}
int parse_command(struct bstrList *splits, Command *cmd)
{
cmd->command = splits->entry[0];
if(biseq(cmd->command, &CREATE)) {
check(splits->qty == 3, "Failed to parse create: %d", splits->qty);
cmd->name = splits->entry[1];
cmd->path = parse_name(cmd->name);
cmd->number = splits->entry[2];
cmd->handler = handle_create;
} else if(biseq(cmd->command, &MEAN)) {
check(splits->qty == 2, "Failed to parse mean: %d", splits->qty);
cmd->name = splits->entry[1];
cmd->path = parse_name(cmd->name);
cmd->handler = handle_mean;
} else if(biseq(cmd->command, &SAMPLE)) {
check(splits->qty == 3, "Failed to parse sample: %d", splits->qty);
cmd->name = splits->entry[1];
cmd->path = parse_name(cmd->name);
cmd->number = splits->entry[2];
cmd->handler = handle_sample;
} else if(biseq(cmd->command, &DUMP)) {
check(splits->qty == 2, "Failed to parse dump: %d", splits->qty);
cmd->name = splits->entry[1];
cmd->path = parse_name(cmd->name);
cmd->handler = handle_dump;
} else if(biseq(cmd->command, &DELETE)) {
check(splits->qty == 2, "Failed to parse delete: %d", splits->qty);
cmd->name = splits->entry[1];
cmd->path = parse_name(cmd->name);
cmd->handler = handle_delete;
} else if(biseq(cmd->command, &STDDEV)) {
check(splits->qty == 2, "Failed to parse stddev: %d", splits->qty);
cmd->name = splits->entry[1];
cmd->path = parse_name(cmd->name);
cmd->handler = handle_stddev;
} else {
sentinel("Failed to parse the command.");
}
return 0;
error:
return 1;
}
int parse_line(bstring data, RingBuffer *send_rb)
{
int rc = 0;
Command cmd = {.command = NULL};
struct bstrList *splits = bsplits(data, &LINE_SPLIT);
check(splits != NULL, "Bad data.");
rc = parse_command(splits, &cmd);
check(rc == 0, "Failed to parse command.");
check(cmd.path->qty > 1, "Didn't give a valid URL.");
// they used a path so break it up
rc = scan_paths(&cmd, send_rb);
check(rc == 0, "Failure running command against path: %s", bdata(cmd.name));
bstrListDestroy(cmd.path);
bstrListDestroy(splits);
return rc;
error:
if(cmd.path) bstrListDestroy(cmd.path);
if(splits) bstrListDestroy(splits);
return -1;
}
int client_handler(int fd)
{
int rc = 0;
RingBuffer *recv_rb = RingBuffer_create(RB_SIZE);
RingBuffer *send_rb = RingBuffer_create(RB_SIZE);
check_mem(recv_rb);
check_mem(send_rb);
while(read_some(recv_rb, fd, 1) != -1) {
// TODO: read a line, put the rest back
bstring data = read_line(recv_rb, LINE_ENDING);
check(data != NULL, "Client closed.");
rc = parse_line(data, send_rb);
check(rc == 0, "Failed to parse user. Closing.");
if(RingBuffer_available_data(send_rb)) {
write_some(send_rb, fd, 1);
}
bdestroy(data);
}
rc = close(fd);
check(rc != -1, "Failed to close fd.");
error: // fallthrough
if(recv_rb) RingBuffer_destroy(recv_rb);
exit(0);
}
void handle_sigchld(int sig) {
if(sig == SIGCHLD) {
while(waitpid(-1, NULL, WNOHANG) > 0) {
}
}
}
int run_server(const char *host, const char *port)
{
struct sockaddr_in client_addr;
socklen_t sin_size = sizeof(client_addr);
int server_socket = 0;
int client_fd = 0;
int rc = 0;
DATA = Hashmap_create(NULL, NULL);
check_mem(DATA);
struct sigaction sa = {
.sa_handler = handle_sigchld,
.sa_flags = SA_RESTART | SA_NOCLDSTOP
};
sigemptyset(&sa.sa_mask);
rc = sigaction(SIGCHLD, &sa, 0);
check(rc != -1, "Failed to setup signal handler for child processes.");
server_socket = server_listen(host, port);
check(server_socket >= 0, "bind to %s:%s failed.",
host, port);
while (1) {
client_fd = accept(server_socket, (struct sockaddr *)&client_addr, &sin_size);
check(client_fd >= 0, "Failed to accept connection.");
debug("Client connected.");
rc = fork();
check(rc != -1, "Failed to fork!");
if(rc == 0) {
// in the child process
close(server_socket);
client_handler(client_fd);
} else {
// server process doesn't need this
close(client_fd);
}
}
return 0;
error:
return -1;
}

View File

@ -0,0 +1,7 @@
#ifndef _statserve_h
#define _statserve_h
int run_server(const char *host, const char *port);
#endif

View File

@ -0,0 +1,33 @@
#undef NDEBUG
#ifndef _minunit_h
#define _minunit_h
#include <stdio.h>
#include <dbg.h>
#include <stdlib.h>
#define mu_suite_start() char *message = NULL
#define mu_assert(test, message) if (!(test)) {\
log_err(message); return message; }
#define mu_run_test(test) debug("\n-----%s", " " #test); \
message = test(); tests_run++; if (message) return message;
#define RUN_TESTS(name) int main(int argc, char *argv[]) {\
argc = 1; \
debug("----- RUNNING: %s", argv[0]);\
printf("----\nRUNNING: %s\n", argv[0]);\
char *result = name();\
if (result != 0) {\
printf("FAILED: %s\n", result);\
}\
else {\
printf("ALL TESTS PASSED\n");\
}\
printf("Tests run: %d\n", tests_run);\
exit(result != 0);\
}
int tests_run;
#endif

View File

@ -0,0 +1,19 @@
echo "Running unit tests:"
for i in tests/*_tests
do
if test -f $i
then
if $VALGRIND ./$i 2>> tests/tests.log
then
echo $i PASS
else
echo "ERROR in test $i: here's tests/tests.log"
echo "------"
tail tests/tests.log
exit 1
fi
fi
done
echo ""

Some files were not shown because too many files have changed in this diff Show More