diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..06b458e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +cfg-sib00_rgncycle.txt diff --git a/README.md b/README.md deleted file mode 100644 index 9f1aafc..0000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# rgncycle-libulfius - -libulfius API for rgncycle \ No newline at end of file diff --git a/cfg.txt b/cfg.txt new file mode 100644 index 0000000..1dd7aed --- /dev/null +++ b/cfg.txt @@ -0,0 +1,8 @@ +pq = +{ +db = "database"; +host = "localhost"; +port = 5432; +secret = "secret"; +user = "user"; +} diff --git a/file2char.c b/file2char.c new file mode 100644 index 0000000..bff1b25 --- /dev/null +++ b/file2char.c @@ -0,0 +1,84 @@ +/*to use exit*/ +#include +/*to use *print**/ +#include +/*to user file2char*/ +#include "file2char.h" + +int convert_file2char(char const* path, char **buf){ + /*declarations*/ + FILE *fp; + long lSize; + + /*TODO Check path for validity!*/ + /*fopen() + *filename + *mode + *return: set errno value in error case + */ + fp=fopen(path,"r"); + + if(fp){ + fprintf(stdout,"file opened.\n"); + + /*set offset to file end*/ + /*fseek() + *stream + *offset + *origin + */ + fseek(fp,0L,SEEK_END); + if (ferror(fp)){ + printf ("Error seeking file\n"); + } + + /*get file size*/ + lSize = ftell( fp ); + fprintf(stdout,"lSize: %ld.\n",lSize); + + /*set offset to file start*/ + fseek(fp,0,SEEK_SET); + if (ferror(fp)){ + printf ("Error seeking file\n"); + } + + /*allocate memory for entire content + null termination*/ + *buf=(char *)malloc((lSize+1)*sizeof(char)); + if(!*buf){ + fclose(fp); + fputs("Error allocating memory",stderr); + exit(EXIT_FAILURE); + } + fprintf(stdout,"mem allocated.\n"); + + /*copy file into buffer*/ + /*fread() + *ptr + *size + *count + *stream + */ + if( 1!=fread(*buf,lSize,1,fp)){ + fclose(fp); + free(*buf); + fputs("Error reading entire file",stderr); + exit(EXIT_FAILURE); + } + fprintf(stdout,"file read.\n"); + + /*close file*/ + if(EOF==fclose(fp)){ + free(*buf); + exit(EXIT_FAILURE); + } + fprintf(stdout,"file closed.\n"); + + (*buf)[lSize] = '\0'; + fprintf(stdout,"mem null terminated.\n"); + }else{ + perror(path); + exit(EXIT_FAILURE); + } + /*done*/ + return 0; +} diff --git a/file2char.h b/file2char.h new file mode 100644 index 0000000..0e244a0 --- /dev/null +++ b/file2char.h @@ -0,0 +1,16 @@ +/*We can use CPP tricks to avoid parsing the same header file more than once*/ +#ifndef FILE2CHAR_H +#define FILE2CHAR_H + +/* + * convert_file2char reads the file identified by 'path' into a character buffer + * pointed at by 'buf'. + * On success, 0 is returned. + * On failure, exit(EXIT_FAILURE) is returned. + * + * WARNING: convert_file2char malloc()s memory to '*buf' which must be freed by + * the caller. + */ +int convert_file2char(char const* path, char **buf); + +#endif /* FILE2CHAR_H */ diff --git a/main.c b/main.c new file mode 100644 index 0000000..50a34a9 --- /dev/null +++ b/main.c @@ -0,0 +1,237 @@ +/*to use *print**/ +#include +/*to create API*/ +#include +/*to connect to Postgresql*/ +#include +/* to use JSON in response body*/ +#include +/*to use struct pq_confo*/ +#include "pqconfo.h" +/*to user file2char*/ +#include "file2char.h" + +#define ROUTE_HELLO "/hello" +#define ROUTE_ENTITIES "/entities" + +/** + * callback functions declaration + */ +int callback_get_hello (const struct _u_request * request, struct _u_response * response, void * user_data); + +int callback_default (const struct _u_request * request, struct _u_response * response, void * user_data); + +int callback_get_entities (const struct _u_request * request, struct _u_response * response, void * user_data); + +int main(int argc, char *argv[]) { + /*declaration*/ + struct _u_instance instance; + int frameworkRet, apiPort, apiPortRet, cx; + /*TODO How is const defined?*/ + const int pqConInfoSize = 100; + char pqConInfo[pqConInfoSize]; + int pqConfoRet; + const struct pq_confo * pqConfo; + + printf("main() Started...\n"); + if (argc != 3) { + fprintf(stderr, "Usage: ./main \n"); + return 1; + } + + printf("main() argv[1]: %s\n", argv[1]); + printf("main() argv[2]: %s\n", argv[2]); + + /*get Postgresql connection info from config*/ + /*TODO Validate argv!*/ + pqConfoRet=get_pq_confo(argv[2],&pqConfo); + if(pqConfoRet){ + fprintf(stderr, "Postgresql connection information are NOT valid\n"); + return 2; + }else{ + fprintf(stdout,"pqConfoRet: %d\n",pqConfoRet); + } + + /*create pq connection info*/ + cx = snprintf(pqConInfo,pqConInfoSize,"postgresql://%s:%s@%s:%d/%s",pqConfo->user,pqConfo->secret,pqConfo->host,pqConfo->port,pqConfo->db); + if(0>cx && cx>=pqConInfoSize){ + fprintf(stderr,"main() error creating pqConInfo\n"); + /*free Postgresql connection info*/ + pqConfoRet=free_pq_confo(&pqConfo); + if(pqConfoRet){ + fprintf(stderr, "Postgresql connection information are NOT free\n"); + return 3; + } + + return 4; + } + + /*free Postgresql connection info*/ + pqConfoRet=free_pq_confo(&pqConfo); + if(pqConfoRet){ + fprintf(stderr, "Postgresql connection information are NOT free\n"); + return 5; + } + + /*store command line argument in variable*/ + /*validate user input*/ + /*omit injection*/ + /*stream:argv*/ + apiPortRet = sscanf(argv[1], "%d", &apiPort); + /*valid user input:1 successfully filled item*/ + if (apiPortRet != 1) { + fprintf(stderr, "The argument must be an integer\n"); + return 6; + } + if (apiPort < 0) { + fprintf(stderr, "Error passing a negative port\n"); + exit(1); + } + printf("main() port: %d\n", apiPort); + + /*TODO Is mem leaked when connection to pq failes?*/ + + if (ulfius_init_instance(&instance, apiPort, NULL, NULL) != U_OK) { + printf("main() Error ulfius_init_instance, abort"); + return 7; + } + + /*connect to database*/ + //TODO This is a synchronous call! Shall we block the rest? + PGconn *conn = PQconnectdb(pqConInfo); + if (PQstatus(conn) == CONNECTION_BAD) { + fprintf(stderr, "Connection to database failed: %s\n", + PQerrorMessage(conn)); + PQfinish(conn); + return 8; + }else if(PQstatus(conn)==CONNECTION_OK){ + printf("main() connected to database\n"); + }else{ + printf("main() connection status NOT known\n"); + } + + //TODO Why? + u_map_put(instance.default_headers, "Access-Control-Allow-Origin", "*"); + + // Maximum body size sent by the client is 1 Kb + instance.max_post_body_size = 1024; + + // Endpoint list declaration + ulfius_add_endpoint_by_val(&instance, "GET", ROUTE_HELLO, NULL, 0, &callback_get_hello, NULL); + //user_data: pass pq connection information to callback + ulfius_add_endpoint_by_val(&instance, "GET", ROUTE_ENTITIES, NULL, 0, &callback_get_entities, (void *)conn); + + // default_endpoint declaration + ulfius_set_default_endpoint(&instance, &callback_default, NULL); + + // Start the framework + // Open an http connection + frameworkRet = ulfius_start_framework(&instance); + + if (frameworkRet == U_OK) { + printf("main() Start framework on port %d\n", instance.port); + /*wait*/ + while(1){ + sleep(3600); + } + } else { + printf("main() Error starting framework; frameworkRet: %d\n",frameworkRet); + } + + printf("main() End framework\n"); + + /*clean up API*/ + ulfius_stop_framework(&instance); + ulfius_clean_instance(&instance); + + /*clean up Postgresql connection*/ + PQfinish(conn); + + printf("main() Done.\n"); + + return 0; +} + +/** + * Callback function that put a "Hello World!" string in the response + */ +int callback_get_hello (const struct _u_request * request, struct _u_response * response, void * user_data) { + ulfius_set_string_body_response(response, 200, "Hello World!"); + return U_CALLBACK_CONTINUE; +} + +/** + * Default callback function called if no endpoint has a match + */ +int callback_default (const struct _u_request * request, struct _u_response * response, void * user_data) { + ulfius_set_string_body_response(response, 404, "Page not found, do what you want"); + return U_CALLBACK_CONTINUE; +} + +/** + * Callback function for route /entities + */ +int callback_get_entities (const struct _u_request * request, struct _u_response * response, void * user_data) { + /*decalarations*/ + PGconn *conn; + char *prepStmEntities; + PGresult *pqRes; + int nbRecords,nbFields; + json_t *arrayRecords, *arrayFields, *stringField; + + printf("callback_get_entities() Started...\n"); + conn = (PGconn *)user_data; + + /*define prepared statement*/ + prepStmEntities = "SELECT * FROM entities;"; + /*create prepared statement*/ + /*conn:connection*/ + /*stm:statement*/ + /*1:number of passed parameters*/ + /*NULL: server figures out parameter type*/ + /*paramValues:pointer of an array of strings containing parameters*/ + /*NULL:relevant for binary parameters*/ + /*NULL:relevant for binary parameters*/ + /*0:obtain result in text format*/ + pqRes = PQexecParams(conn, prepStmEntities, 0, NULL, NULL, NULL, NULL, 0); + if (PQresultStatus(pqRes) != PGRES_TUPLES_OK) { + printf("callback_get_entities() No data retrieved\n"); + /*clean up result*/ + PQclear(pqRes); + //TODO Why this casting? + (void)(request); + (void)(user_data); + ulfius_set_string_body_response(response, 200, "This is the /entities route: Dba error!"); + printf("callback_get_entities() Done.\n"); + return U_CALLBACK_CONTINUE; + } + + /*evaluate result*/ + nbRecords = PQntuples(pqRes); + nbFields = PQnfields(pqRes); + arrayRecords=json_array(); + for(int i=0;i +/*to use libconfig*/ +#include +/*to use struct pq_confo*/ +#include "pqconfo.h" + +/*get Postgresql connection information*/ +int get_pq_confo(const char * file,const struct pq_confo ** confo){ + /*declaration*/ + config_t cfg; + const char * db, * host, * secret, * user; + int port; + + fprintf(stdout,"get_pq_confo() Started...\n"); + /*initialise new and EMPTY config*/ + config_init(&cfg); + + /* Read the file. If there is an error, report it and exit. */ + /*TODO Validate config file name!*/ + if(! config_read_file(&cfg, file)) + { + fprintf(stderr, "get_pq_confo() %s:%d - %s\n", config_error_file(&cfg), + config_error_line(&cfg), config_error_text(&cfg)); + /*destroy config, BUT NOT the structure*/ + config_destroy(&cfg); + return 2; + } + fprintf(stdout,"get_pq_confo() Parsing config file done\n"); + + /*lookup db string*/ + if(config_lookup_string(&cfg, "pq.db", &db)){ + printf("get_pq_confo() db: %s\n", db); + }else{ + fprintf(stderr, "get_pq_confo() NO 'db' setting in config file found.\n"); + return 3; + } + + /*lookup host string*/ + if(config_lookup_string(&cfg, "pq.host", &host)){ + printf("get_pq_confo() host: %s\n", host); + }else{ + fprintf(stderr, "get_pq_confo() NO 'host' setting in config file found.\n"); + return 4; + } + + /*lookup secret string*/ + if(!config_lookup_string(&cfg, "pq.secret", &secret)){ + fprintf(stderr, "get_pq_confo() NO 'secret' setting in config file found.\n"); + return 5; + } + + /*lookup user string*/ + if(config_lookup_string(&cfg, "pq.user", &user)){ + printf("get_pq_confo() user: %s\n", user); + }else{ + fprintf(stderr, "get_pq_confo() NO 'user' setting in config file found.\n"); + return 6; + } + + /*lookup port int*/ + if(config_lookup_int(&cfg, "pq.port", &port)){ + printf("get_pq_confo() port: %d\n", port); + }else{ + fprintf(stderr, "get_pq_confo() NO 'port' setting in config file found.\n"); + return 7; + } + + /*create struct*/ + struct pq_confo * pqConfo = (struct pq_confo *)malloc( sizeof(struct pq_confo)); + sprintf(pqConfo->db,"%s",db); + sprintf(pqConfo->host,"%s",host); + pqConfo->port=port; + sprintf(pqConfo->secret,"%s",secret); + sprintf(pqConfo->user,"%s",user); + *confo = pqConfo; + + /*destroy config, BUT NOT the structure*/ + config_destroy(&cfg); + + /*return success*/ + return 0; +} + +/*free Postgresql connection information*/ +int free_pq_confo(const struct pq_confo ** confo){ + free((struct pq_confo *)*confo); + return 0; +} diff --git a/pqconfo.h b/pqconfo.h new file mode 100644 index 0000000..24eb475 --- /dev/null +++ b/pqconfo.h @@ -0,0 +1,21 @@ +/*We can use CPP tricks to avoid parsing the same header file more than once*/ +#ifndef PQCONFO_H +# define PQCONFO_H + +/*structure for Postgresql connection information*/ +struct pq_confo +{ + char db[23]; + char host[23]; + int port; + char secret[42]; + char user[23]; +}; + +/*get Postgresql connection information*/ +int get_pq_confo(const char * file,const struct pq_confo ** confo); + +/*free Postgresql connection information*/ +int free_pq_confo(const struct pq_confo ** confo); + +#endif /* PQCONFO_H */ diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..fed76da --- /dev/null +++ b/readme.md @@ -0,0 +1,21 @@ +# rgncycle-libulfius + +libulfius API for rgncycle + +* build + +``` +make +``` + +* run + +``` +./main +``` + +* run Valgrind tool `Memcheck` + +``` +valgrind --leak-check=yes -s ./main +```