sds

simple/sensor data server
git clone git://git.hanetzok.net/sds
Log | Files | Refs | README | LICENSE

sds.c (5622B)


      1 /* 
      2    Copyright 2024 Markus Hanetzok
      3 
      4    This program is free software: you can redistribute it and/or modify it under
      5    the terms of the GNU General Public License as published by the Free Software
      6    Foundation, either version 3 of the License, or (at your option) any later
      7    version.
      8 
      9    This program is distributed in the hope that it will be useful, but WITHOUT
     10    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
     11    FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   this program. If not, see <https://www.gnu.org/licenses/>. 
     15 */
     16 
     17 #include <ctype.h>
     18 #include <stdio.h>
     19 #include <stdlib.h>
     20 #include <string.h>
     21 #include <unistd.h>
     22 #include <arpa/inet.h>
     23 #include <curl/curl.h>
     24 #include <sys/socket.h>
     25 
     26 
     27 #define BUFFER_SIZE 1024
     28 #define OK_HEADER "HTTP/1.0 200 OK\r\nContent-Type:text/html\r\n\r\n"
     29 #define NOTFOUND_HEADER "HTTP/1.0 404 Not Found\r\nContent-Type:text/html\r\n\r\n"
     30 #define NOTALLOWED_HEADER "HTTP/1.0 405 Method Not Allowed\r\nContent-Type:text/html\r\n\r\n"
     31 #define INTERNALERR_HEADER "HTTP/1.0 500 Internal Server Error\r\nContent-Type:text/html\r\n\r\n"
     32 
     33 typedef struct {
     34   char *name;
     35   char *addr;
     36   char *unit;
     37 } Sensor;
     38 
     39 typedef struct {
     40   char *memory;
     41   size_t size;
     42 } MemoryStruct;
     43 
     44 void die(char *msg) {
     45   printf("%s\n", msg);
     46   exit(1);
     47 }
     48 
     49 void buildsite(char response[], char *name, char *val, char *unit);
     50 size_t writemem(void *contents, size_t size, size_t nmemb, void *userp);
     51 void writeresponse(char response[], int clientfd, size_t responselen);
     52 
     53 #include "sensors.h"
     54 
     55 void buildsite(char response[], char *name, char *val, char *unit) {
     56   snprintf(response + strlen(response), BUFFER_SIZE - strlen(response), 
     57       "<li>%s: %s%s</li>", name, val, unit);
     58 }
     59 
     60 size_t writemem(void *contents, size_t size, size_t nmemb, void *userp) {
     61   size_t realsize = size * nmemb;
     62   MemoryStruct *mem = (MemoryStruct *)userp;
     63 
     64   char *ptr = realloc(mem->memory, mem->size + realsize + 1);
     65   if (!ptr) {
     66     fprintf(stderr, "not enough memory (realloc returned NULL)\n");
     67     return 0;
     68   }
     69   mem->memory = ptr;
     70   memcpy(&(mem->memory[mem->size]), contents, realsize);
     71   mem->size += realsize;
     72   mem->memory[mem->size] = 0;
     73 
     74   return realsize;
     75 }
     76 
     77 void writeresponse(char response[], int clientfd, size_t responselen) {
     78   write(clientfd, response, strlen(response));
     79   close(clientfd);
     80 }
     81 
     82 int main(int argc, char *argv[]) {
     83   if (argc < 3 || strcmp(argv[1], "-p"))
     84     die("usage: sds [-p PORT]");
     85   char response[BUFFER_SIZE];
     86   int sockfd = socket(AF_INET, SOCK_STREAM, 0);
     87   int port = atoi(argv[2]);
     88   char buffer[BUFFER_SIZE];
     89 
     90   struct sockaddr_in host_addr;
     91   int host_addrlen = sizeof(host_addr);
     92   host_addr.sin_family = AF_INET;
     93   host_addr.sin_port = htons(port);
     94   host_addr.sin_addr.s_addr = htonl(INADDR_ANY);
     95 
     96   struct sockaddr_in client_addr;
     97   int client_addrlen = sizeof(client_addr);
     98 
     99   if (bind(sockfd, (struct sockaddr *)&host_addr, host_addrlen) != 0) 
    100     die("could not bind socket");
    101   if (listen(sockfd, 10) != 0)
    102     die("could not listen");
    103 
    104   while(1) {
    105     int clientfd = accept(sockfd, (struct sockaddr *)&host_addr, 
    106         (socklen_t *)&host_addrlen);
    107     if (clientfd < 0) {
    108       fprintf(stderr, "could not accept connection\n");
    109       continue;
    110     }
    111     getpeername(clientfd, (struct sockaddr *)&client_addr, 
    112         (socklen_t *)&client_addrlen);
    113 
    114     memset(buffer, 0, BUFFER_SIZE);
    115     read(clientfd, buffer, BUFFER_SIZE);
    116     char method[BUFFER_SIZE], uri[BUFFER_SIZE], version[BUFFER_SIZE];
    117     sscanf(buffer, "%s %s %s", method, uri, version);
    118     printf("[%s:%u] %s %s %s\n", inet_ntoa(client_addr.sin_addr), 
    119         ntohs(client_addr.sin_port), method, version, uri);
    120 
    121     memset(response, 0, BUFFER_SIZE);
    122 
    123     if (strcmp("GET", method)) {
    124       printf("%s\n", uri);
    125       snprintf(response + strlen(response), BUFFER_SIZE - strlen(response),
    126           "%s%s", NOTALLOWED_HEADER, "<html>method not allowed</html>");
    127       writeresponse(response, clientfd, strlen(response));
    128       continue;
    129     }
    130     if (strcmp("/", uri)) {
    131       printf("%s\n", uri);
    132       snprintf(response + strlen(response), BUFFER_SIZE - strlen(response),
    133           "%s%s", NOTFOUND_HEADER, "<html>page not found</html>");
    134       writeresponse(response, clientfd, strlen(response));
    135       continue;
    136     }
    137     snprintf(response + strlen(response), BUFFER_SIZE - strlen(response), 
    138         "%s%s", OK_HEADER, "<html><h1>Sensor data</h1><ul>");
    139     int sensorsc = sizeof(sensors)/sizeof(sensors[0]);
    140     int i;
    141     for (i = 0; i < sensorsc; i++) {
    142       char *name = sensors[i].name;
    143       char *unit = sensors[i].unit;
    144       CURL *curl;
    145       CURLcode curlresponse;
    146 
    147       MemoryStruct chunk;
    148       chunk.memory = malloc(1);
    149       chunk.size = 0;
    150 
    151 
    152       curl = curl_easy_init();
    153       if (curl == NULL) {
    154         fprintf(stderr, "request failed\n");
    155         continue;
    156       }
    157 
    158       curl_easy_setopt(curl, CURLOPT_URL, sensors[i].addr);
    159       curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writemem);
    160       curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
    161       curlresponse = curl_easy_perform(curl);
    162       if (curlresponse != CURLE_OK) {
    163         fprintf(stderr, "request failed\n");
    164         continue;
    165       }
    166 
    167       char *val = chunk.memory;
    168       buildsite(response, name, val, unit);
    169 
    170       free(chunk.memory);
    171       curl_easy_cleanup(curl);
    172 
    173     }
    174     snprintf(response + strlen(response), BUFFER_SIZE - strlen(response), 
    175         "</ul></html>");
    176 
    177     writeresponse(response, clientfd, strlen(response));
    178   }
    179   return 0;
    180 }
    181