Remaining files for the videos, minus the statserve final.
This commit is contained in:
parent
026a100a92
commit
4305a58bb9
|
@ -1,3 +1,8 @@
|
|||
Videos
|
||||
.*.sw*
|
||||
*.pdf
|
||||
*.dSYM
|
||||
*.so
|
||||
*.o
|
||||
*.log
|
||||
*.db
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
|
@ -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
|
|
@ -0,0 +1,7 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
||||
all: ex1
|
||||
|
||||
clean:
|
||||
rm -f ex1
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
rm ex20
|
||||
make ex20
|
||||
./ex20 1 82 39 44 1 2 3 4 9 8 01 10 11
|
|
@ -0,0 +1,4 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
||||
all:
|
||||
cc $(CFLAGS) -o ex21 ex21.c ex21_main.c
|
|
@ -0,0 +1,2 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
|
@ -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
|
|
@ -0,0 +1,6 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
||||
all: logfind
|
||||
./logfind || true
|
||||
./logfind test test test
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
||||
all: logfind
|
||||
./logfind || true
|
||||
./logfind error
|
||||
|
||||
clean:
|
||||
rm -f logfind
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
*.c
|
||||
*.h
|
||||
Makefile
|
|
@ -0,0 +1,8 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
||||
all: logfind
|
||||
./logfind || true
|
||||
./logfind error
|
||||
|
||||
clean:
|
||||
rm -f logfind
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
*.c
|
||||
*.h
|
||||
Makefile
|
|
@ -0,0 +1,8 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
||||
all: logfind
|
||||
./logfind || true
|
||||
./logfind MAX_LINE
|
||||
|
||||
clean:
|
||||
rm -f logfind
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
*.c
|
||||
*.h
|
||||
Makefile
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
*.c
|
||||
*.h
|
||||
Makefile
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
CFLAGS=-Wall -g
|
||||
|
||||
clean:
|
||||
rm -f ex3
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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
|
|
@ -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 ""
|
|
@ -0,0 +1,8 @@
|
|||
*.o
|
||||
*.dSYM
|
||||
*.so
|
||||
.*.sw*
|
||||
*.log
|
||||
build/*
|
||||
tests/*_tests
|
||||
bin/statserve
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef _statserve_h
|
||||
#define _statserve_h
|
||||
|
||||
int echo_server(const char *host, const char *port);
|
||||
|
||||
|
||||
#endif
|
|
@ -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
|
|
@ -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 ""
|
|
@ -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);
|
|
@ -0,0 +1,8 @@
|
|||
*.o
|
||||
*.dSYM
|
||||
*.so
|
||||
.*.sw*
|
||||
*.log
|
||||
build/*
|
||||
tests/*_tests
|
||||
bin/statserve
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef _statserve_h
|
||||
#define _statserve_h
|
||||
|
||||
int echo_server(const char *host, const char *port);
|
||||
|
||||
|
||||
#endif
|
|
@ -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
|
|
@ -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 ""
|
|
@ -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);
|
|
@ -0,0 +1,7 @@
|
|||
*.o
|
||||
*.dSYM
|
||||
*.so
|
||||
.*.sw*
|
||||
*.log
|
||||
build/*
|
||||
tests/*_tests
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef _statserve_h
|
||||
#define _statserve_h
|
||||
|
||||
int run_server(const char *host, const char *port);
|
||||
|
||||
|
||||
#endif
|
|
@ -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
|
|
@ -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
Loading…
Reference in New Issue