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