feat: initial commit

This commit is contained in:
dancingCycle 2023-03-15 17:50:14 +01:00
parent 1fe1a3c4ab
commit 214680db9c
10 changed files with 502 additions and 3 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
cfg-sib00_rgncycle.txt

View File

@ -1,3 +0,0 @@
# rgncycle-libulfius
libulfius API for rgncycle

8
cfg.txt Normal file
View File

@ -0,0 +1,8 @@
pq =
{
db = "database";
host = "localhost";
port = 5432;
secret = "secret";
user = "user";
}

84
file2char.c Normal file
View File

@ -0,0 +1,84 @@
/*to use exit*/
#include <stdlib.h>
/*to use *print**/
#include <stdio.h>
/*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;
}

16
file2char.h Normal file
View File

@ -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 */

237
main.c Normal file
View File

@ -0,0 +1,237 @@
/*to use *print**/
#include <stdio.h>
/*to create API*/
#include <ulfius.h>
/*to connect to Postgresql*/
#include <libpq-fe.h>
/* to use JSON in response body*/
#include <jansson.h>
/*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 <API port> <path to config file>\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<nbRecords;i++){
arrayFields=json_array();
for(int j=0;j<nbFields;j++){
/*not password, verification_token_id, created_account*/
if(j!=6&&j!=8&&j!=10){
stringField = json_string(PQgetvalue(pqRes,i,j));
json_array_append(arrayFields, stringField);
json_decref(stringField);
}
}
json_array_append(arrayRecords, arrayFields);
json_decref(arrayFields);
}
/*clean up result*/
PQclear(pqRes);
/*set response body*/
ulfius_set_json_body_response(response, 200, arrayRecords);
/*clean up JSON*/
json_decref(arrayRecords);
//TODO Why this casting?
(void)(request);
(void)(user_data);
printf("callback_get_entities() Done.\n");
return U_CALLBACK_CONTINUE;
}

25
makefile Normal file
View File

@ -0,0 +1,25 @@
#target: dependency_1 dependency_2 dependency_3 ...
# command
#
RM = /bin/rm -f
OBJ = main.o pqconfo.o file2char.o
EXE = main
CC = /usr/bin/gcc
CFLAGS = -Wall
#
all: $(EXE)
#
$(EXE): $(OBJ)
$(CC) $(CFLAGS) $(OBJ) -L/usr/lib/x86_64-linux-gnu -lconfig -lulfius -lpq -ljansson -o $(EXE)
#
pqconfo.o: pqconfo.c pqconfo.h
$(CC) -c pqconfo.c -o pqconfo.o
#
file2char.o: file2char.c file2char.h
$(CC) -c file2char.c -o file2char.o
#
main.o: main.c
$(CC) -I/usr/include/postgresql -c main.c -o main.o
#
clean:
$(RM) $(OBJ) $(EXE) *~

89
pqconfo.c Normal file
View File

@ -0,0 +1,89 @@
/*to use malloc*/
#include <stdlib.h>
/*to use libconfig*/
#include <libconfig.h>
/*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;
}

21
pqconfo.h Normal file
View File

@ -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 */

21
readme.md Normal file
View File

@ -0,0 +1,21 @@
# rgncycle-libulfius
libulfius API for rgncycle
* build
```
make
```
* run
```
./main
```
* run Valgrind tool `Memcheck`
```
valgrind --leak-check=yes -s ./main
```