SDDS ToolKit Programs and Libraries for C and Python
Loading...
Searching...
No Matches
sddslogserver.c
Go to the documentation of this file.
1/**
2 * @file sddslogserver.c
3 * @brief Server program to log data to SDDS files.
4 *
5 * @details
6 * This program listens on a specified port and handles multiple client connections
7 * to log data into SDDS files. It supports various commands such as adding values,
8 * creating channels, making directories, and generating SDDS plots. Key features include:
9 * - Handling multiple client connections using forked processes.
10 * - Dynamic creation and management of SDDS channels.
11 * - Command restrictions through the forbid option.
12 * - Generating SDDS plots and returning their URLs.
13 *
14 * @section Usage
15 * ```
16 * sddslogserver -port=<portNumber>
17 * [-root=<rootDirectory>]
18 * [-forbid=<command1,command2,...>]
19 * [-sddsplotPath=<path>]
20 * ```
21 *
22 * @section Options
23 * | Required | Description |
24 * |---------------------------------------|---------------------------------------------------------|
25 * | `-port` | Port number on which the server listens. |
26 *
27 * | Optional | Description |
28 * |---------------------------------------|---------------------------------------------------------|
29 * | `-root` | Path of the root directory. Defaults to current directory. |
30 * | `-forbid` | Comma-separated list of commands to forbid. |
31 * | `-sddsplotPath` | Pathname for SDDS plot output files. |
32 *
33 * @subsection Specific Requirements
34 * - `-port` must be a valid positive integer.
35 * - `-root` must specify an existing directory.
36 * - `-sddsplotPath` requires a valid directory path.
37 *
38 * @copyright
39 * - (c) 2002 The University of Chicago, as Operator of Argonne National Laboratory.
40 * - (c) 2002 The Regents of the University of California, as Operator of Los Alamos National Laboratory.
41 *
42 * @license
43 * This file is distributed under the terms of the Software License Agreement
44 * found in the file LICENSE included with this distribution.
45 *
46 * @authors
47 * M. Borland,
48 * R. Soliday
49 */
50
51#include <stdio.h>
52#include <unistd.h>
53#include <stdlib.h>
54#include <string.h>
55#include <sys/types.h>
56#include <sys/socket.h>
57#include <netinet/in.h>
58#include <signal.h>
59#include <sys/stat.h>
60#include <dirent.h>
61
62#include "mdb.h"
63#include "SDDS.h"
64#include "scan.h"
65
66#define BUFLEN 16384
67
68int dostuff(int);
69int createChannel(char *spec);
70int addValue(char *spec);
71int runSddsplot(char *returnBuffer, char *options);
72void updateChannelDescription(void);
73int makeDirectoryList(int64_t *returnNumber, char ***returnBuffer);
74int getChannelList(int64_t *returnNumber, char ***returnBuffer);
75
76char *rootDir;
77
78#define DISCONNECT 0 /* Disconnect from the server—forces server to terminate the forked process */
79#define ADD_VALUE 1
80#define MAKE_DIRECTORY 2
81#define CHANGE_DIRECTORY 3
82#define GET_TIME_SPAN 4 /* Get values between two times */
83#define GET_LAST_N 5 /* Get last N values */
84#define SDDSPLOT 6 /* Make an sddsplot and return its URL */
85#define ADD_CHANNEL 7
86#define DELETE_VALUE 8
87#define UPDATE_CHD 9
88#define LIST_DIRS 10
89#define LIST_CHANNELS 11
90#define N_COMMANDS 12
91
92char *command[N_COMMANDS] = {
93 "disconnect",
94 "addValue",
95 "mkdir",
96 "cd",
97 "getTimeSpan",
98 "getLastN",
99 "sddsplot",
100 "addChannel",
101 "deleteValue",
102 "updateChDesc",
103 "listDirs",
104 "listChannels",
105};
106
107short forbid[N_COMMANDS] = {
108 0,
109 0,
110 0,
111 0,
112 0,
113 0,
114 0,
115 0,
116 0,
117 0,
118 0,
119 0,
120};
121
122void error(const char *msg, char *progName) {
123 char s[BUFLEN];
124 snprintf(s, sizeof(s), "%s (%s)", msg, progName);
125 perror(s);
126 exit(EXIT_FAILURE);
127}
128
129int writeReply(int sock, const char *message, int code) {
130 char buffer[BUFLEN + 20];
131 if (code) {
132 snprintf(buffer, sizeof(buffer), "error:%s (code %d)\n", message, code);
133 return write(sock, buffer, strlen(buffer));
134 } else {
135 // snprintf(buffer, sizeof(buffer), "ok:%s\n", message);
136 return write(sock, message, strlen(message));
137 }
138}
139
140int writeReplyList(int sock, int64_t nItems, char **messageList) {
141 int64_t i;
142 char message[BUFLEN];
143 strncpy(message, "ok:", sizeof(message) - 1);
144 message[sizeof(message) - 1] = 0;
145
146 /* Need to check for buffer overflow */
147 printf("%" PRId64 " reply items\n", nItems);
148 for (i = 0; i < nItems; i++) {
149 printf("Item %" PRId64 ": %s\n", i, messageList[i]);
150 strncat(message, messageList[i], sizeof(message) - strlen(message) - 1);
151 if (i != (nItems - 1))
152 strncat(message, ",", sizeof(message) - strlen(message) - 1);
153 }
154 strncat(message, "\n", sizeof(message) - strlen(message) - 1);
155 printf("message: %s", message);
156 return write(sock, message, strlen(message));
157}
158
159void freeReplyList(int64_t nItems, char **item) {
160 int64_t i;
161 if (!item)
162 return;
163 for (i = 0; i < nItems; i++)
164 if (item[i])
165 free(item[i]);
166 free(item);
167}
168
169int chdirFromRoot(char *path) {
170 char buffer[BUFLEN];
171 snprintf(buffer, sizeof(buffer), "%s/%s", rootDir, (path && *path) ? path : "");
172 fprintf(stderr, "Changing directory to %s\n", buffer);
173 return chdir(buffer);
174}
175
176#define CLI_PORT 0
177#define CLI_ROOT 1
178#define CLI_FORBID 2
179#define CLI_SDDSPLOT_PATH 3
180#define N_OPTIONS 4
181
182char *option[N_OPTIONS] = {
183 "port",
184 "root",
185 "forbid",
186 "sddsplotpath"
187};
188
189const char *USAGE =
190 "Usage: sddslogserver -port=<portNumber> [-root=<rootDirectory>] \n"
191 " [-forbid=<command1,command2,...>] \n"
192 " [-sddsplotPath=<path>]\n\n"
193 "Options:\n"
194 " -port Port number on which the server listens (required).\n"
195 " -root Path of the root directory (optional, defaults to current directory).\n"
196 " -forbid Comma-separated list of commands to forbid (optional).\n"
197 " -sddsplotPath Pathname for SDDS plot output files (optional).\n\n"
198 "Program by Michael Borland. (" __DATE__ " " __TIME__ ", SVN revision: " SVN_VERSION ")\n";
199
200char *sddsplotPath = NULL;
201
202static int sockfd = -1;
203
204void shutdownServer(int arg) {
205 printf("Closing sockfd=%d\n", sockfd);
206 fflush(stdout);
207 if (sockfd >= 0)
208 close(sockfd);
209 exit(EXIT_SUCCESS);
210}
211
212int main(int argc, char *argv[]) {
213 int newsockfd, portno, pid;
214 int reuse = 1;
215 socklen_t clilen;
216 struct sockaddr_in serv_addr, cli_addr;
217 int i_arg, j, code;
218 SCANNED_ARG *s_arg;
219
220 /* Allow zombie children to die */
221 signal(SIGCHLD, SIG_IGN);
222 signal(SIGINT, shutdownServer);
223
224 /* Parse arguments */
226 argc = scanargs(&s_arg, argc, argv);
227 if (argc < 2) {
228 fprintf(stderr, "%s", USAGE);
229 exit(EXIT_FAILURE);
230 }
231
232 portno = -1;
233 rootDir = NULL;
234 for (i_arg = 1; i_arg < argc; i_arg++) {
235 if (s_arg[i_arg].arg_type == OPTION) {
236 switch (match_string(s_arg[i_arg].list[0], option, N_OPTIONS, 0)) {
237 case CLI_PORT:
238 if (s_arg[i_arg].n_items != 2 ||
239 sscanf(s_arg[i_arg].list[1], "%d", &portno) != 1 ||
240 portno <= 0) {
241 fprintf(stderr, "Error: Invalid syntax/values for -port argument\n%s", USAGE);
242 exit(EXIT_FAILURE);
243 }
244 break;
245 case CLI_ROOT:
246 if (s_arg[i_arg].n_items != 2 ||
247 strlen(rootDir = s_arg[i_arg].list[1]) == 0) {
248 fprintf(stderr, "Error: Invalid syntax/values for -root argument\n%s", USAGE);
249 exit(EXIT_FAILURE);
250 }
251 break;
252 case CLI_FORBID:
253 if (s_arg[i_arg].n_items < 2) {
254 fprintf(stderr, "Error: Invalid syntax/values for -forbid argument\n%s", USAGE);
255 exit(EXIT_FAILURE);
256 }
257 for (j = 1; j < s_arg[i_arg].n_items; j++) {
258 if ((code = match_string(s_arg[i_arg].list[j], command, N_COMMANDS, 0)) < 0) {
259 fprintf(stderr, "Error: Unknown command for -forbid: %s\n", s_arg[i_arg].list[j]);
260 exit(EXIT_FAILURE);
261 }
262 forbid[code] = 1;
263 }
264 break;
265 case CLI_SDDSPLOT_PATH:
266 if (s_arg[i_arg].n_items != 2) {
267 fprintf(stderr, "Error: Invalid syntax for -sddsplotPath option\n%s", USAGE);
268 exit(EXIT_FAILURE);
269 }
270 sddsplotPath = s_arg[i_arg].list[1];
271 break;
272 default:
273 fprintf(stderr, "Invalid or ambiguous option: %s\n%s", s_arg[i_arg].list[0], USAGE);
274 exit(EXIT_FAILURE);
275 break;
276 }
277 } else {
278 fprintf(stderr, "Invalid or ambiguous option: %s\n%s", s_arg[i_arg].list[0], USAGE);
279 exit(EXIT_FAILURE);
280 }
281 }
282
283 if (!rootDir) {
284 cp_str(&rootDir, ".");
285 }
286 if (!fexists(rootDir))
287 error("Error: Root directory not found", argv[0]);
288 if (chdir(rootDir) != 0)
289 perror("chdir");
290
291 /* Create socket */
292 sockfd = socket(AF_INET, SOCK_STREAM, 0);
293 if (sockfd < 0)
294 error("Error opening socket", argv[0]);
295 printf("sockfd = %d\n", sockfd);
296 memset(&serv_addr, 0, sizeof(serv_addr));
297
298 /* Set the socket to SO_REUSEADDR */
299 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse, sizeof(reuse)) < 0)
300 perror("setsockopt(SO_REUSEADDR) failed");
301
302#ifdef SO_REUSEPORT
303 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (const char *)&reuse, sizeof(reuse)) < 0)
304 perror("setsockopt(SO_REUSEPORT) failed");
305#endif
306
307 /* Bind the socket to a port number */
308 serv_addr.sin_family = AF_INET;
309 serv_addr.sin_addr.s_addr = INADDR_ANY;
310 serv_addr.sin_port = htons(portno);
311 if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
312 error("Error on port binding. Check port number.", argv[0]);
313
314 listen(sockfd, 5);
315 clilen = sizeof(cli_addr);
316 while (1) {
317 printf("Waiting for new socket connection\n");
318 fflush(stdout);
319 newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
320 printf("Got new socket connection\n");
321 fflush(stdout);
322 if (newsockfd < 0) {
323 if (sockfd >= 0)
324 close(sockfd);
325 error("Error on accept", argv[0]);
326 }
327 pid = fork();
328 if (pid < 0)
329 error("Error on fork", argv[0]);
330 if (pid == 0) {
331 close(sockfd);
332 dostuff(newsockfd);
333 printf("Returned from dostuff\n");
334 fflush(stdout);
335 exit(EXIT_SUCCESS);
336 } else {
337 printf("Forked process\n");
338 fflush(stdout);
339 close(newsockfd);
340 }
341 } /* end of while */
342 printf("Exited while loop\n");
343 fflush(stdout);
344 if (sockfd > 0)
345 close(sockfd);
346 return EXIT_SUCCESS; /* We never get here */
347}
348
349/******** DOSTUFF() *********************
350 There is a separate instance of this function
351 for each connection. It handles all communication
352 once a connection has been established.
353*****************************************/
354
355int dostuff(int sock) {
356 int n, code;
357 char buffer[BUFLEN];
358 char *ptr1;
359 short persist = 1;
360 int64_t nItems;
361 char **itemValue;
362
363 while (persist) {
364 memset(buffer, 0, BUFLEN);
365 n = read(sock, buffer, BUFLEN - 1);
366 if (n < 0)
367 error("ERROR reading from socket", "sddslogserver");
368 if (strlen(buffer) == 0)
369 continue;
370 chop_nl(buffer);
371 printf("Here is the message: <%s>\n", buffer);
372
373 if ((ptr1 = strchr(buffer, ':')))
374 *ptr1++ = 0;
375 if ((code = match_string(buffer, command, N_COMMANDS, EXACT_MATCH)) < 0 || forbid[code]) {
376 writeReply(sock, "Forbidden operation.", 0);
377 continue;
378 }
379 switch (code) {
380 case DISCONNECT:
381 printf("Disconnecting\n");
382 fflush(stdout);
383 persist = 0;
384 break;
385 case ADD_VALUE:
386 /* syntax: channel=value */
387 printf("Add value: %s\n", ptr1);
388 if ((code = addValue(ptr1)) == 0)
389 writeReply(sock, "ok", code);
390 else
391 writeReply(sock, "Error: Failed to add value.", code);
392 break;
393 case DELETE_VALUE:
394 /* syntax: channel,sampleID */
395 printf("Delete value: %s\n", ptr1);
396 writeReply(sock, "Error: Can't do that yet.", EXIT_FAILURE);
397 break;
398 case ADD_CHANNEL:
399 /* syntax: channel,type */
400 printf("Add channel: %s\n", ptr1);
401 if ((code = createChannel(ptr1)) == 0)
402 writeReply(sock, "ok", code);
403 else
404 writeReply(sock, "Error: Failed to create channel.", code);
405 break;
406 case MAKE_DIRECTORY:
407 /* syntax: directoryName */
408 printf("Make directory: %s\n", ptr1);
409 if ((code = mkdir(ptr1, 0700))) {
410 writeReply(sock, "Error: Making directory.", code);
411 } else {
412 writeReply(sock, "ok", code);
413 }
414 break;
415 case CHANGE_DIRECTORY:
416 /* syntax: path */
417 if (ptr1 && *ptr1 && strstr(ptr1, "..")) {
418 /* Only absolute paths are allowed */
419 writeReply(sock, "Error: Relative paths not supported.", EXIT_FAILURE);
420 } else {
421 printf("Change directory: %s\n", ptr1 && *ptr1 ? ptr1 : "base");
422 if ((code = chdirFromRoot(ptr1)))
423 writeReply(sock, "Error: CD failed.", code);
424 else
425 writeReply(sock, "CD ok.", code);
426 }
427 break;
428 case GET_TIME_SPAN:
429 /* syntax: channel,startTime,endTime */
430 printf("Get time span: %s\n", ptr1);
431 writeReply(sock, "Error: Can't do that yet.", EXIT_FAILURE);
432 break;
433 case GET_LAST_N:
434 /* syntax: channel,N; if N<=0, return all data */
435 printf("Get last N: %s\n", ptr1);
436 writeReply(sock, "Error: Can't do that yet.", EXIT_FAILURE);
437 break;
438 case SDDSPLOT:
439 /* syntax:<sddsplotCommand> */
440 printf("sddsplot: %s\n", ptr1);
441 code = runSddsplot(buffer, ptr1);
442 writeReply(sock, buffer, code);
443 break;
444 case UPDATE_CHD:
445 updateChannelDescription();
446 writeReply(sock, "ok", EXIT_SUCCESS);
447 break;
448 case LIST_DIRS:
449 if (makeDirectoryList(&nItems, &itemValue))
450 writeReply(sock, "Failed to retrieve directory list.", EXIT_FAILURE);
451 else {
452 writeReplyList(sock, nItems, itemValue);
453 freeReplyList(nItems, itemValue);
454 }
455 break;
456 case LIST_CHANNELS:
457 if (getChannelList(&nItems, &itemValue))
458 writeReply(sock, "Failed to retrieve channel list.", EXIT_FAILURE);
459 else {
460 writeReplyList(sock, nItems, itemValue);
461 freeReplyList(nItems, itemValue);
462 }
463 break;
464 default:
465 printf("Unknown command: %s\n", buffer);
466 writeReply(sock, "Error: Unknown command.", EXIT_FAILURE);
467 break;
468 }
469 }
470 return EXIT_SUCCESS;
471}
472
473int createChannel(char *spec)
474/* spec is of the form <channelName>,<type>,<units>,<description> */
475{
476 char *chName, *chType, *chUnits, *chDescription;
477 char buffer[BUFLEN];
478 SDDS_DATASET SDDSout;
479 int32_t type;
480
481 chName = spec;
482 if (!(chType = strchr(spec, ',')))
483 return 1;
484 *chType++ = 0;
485 if (!(chUnits = strchr(chType, ',')))
486 return 2;
487 *chUnits++ = 0;
488 if ((type = SDDS_IdentifyType(chType)) == 0)
489 return 2;
490 if (!(chDescription = strchr(chUnits, ',')))
491 return 3;
492 *chDescription++ = 0;
493
494 if (strcmp(chName, "Time") == 0)
495 return 4;
496
497 snprintf(buffer, sizeof(buffer), "%s.sdds", chName);
498 if (fexists(buffer))
499 return 5;
500
501 if (!SDDS_InitializeOutput(&SDDSout, SDDS_BINARY, 0, NULL, NULL, buffer) ||
502 !SDDS_DefineSimpleColumn(&SDDSout, "SampleIDNumber", NULL, SDDS_LONG64) ||
503 !SDDS_DefineSimpleColumn(&SDDSout, "Time", "s", SDDS_DOUBLE) ||
504 SDDS_DefineColumn(&SDDSout, chName, NULL, chUnits, chDescription, NULL, type, 0) < 0 ||
505 !SDDS_WriteLayout(&SDDSout) ||
506 !SDDS_StartPage(&SDDSout, 1) ||
507 !SDDS_WritePage(&SDDSout) ||
508 !SDDS_Terminate(&SDDSout))
509 return 6;
510
511 snprintf(buffer, sizeof(buffer), "sddsquery %s.sdds -sddsOutput=%s.chd -column", chName, chName);
512 {
513 int sysret = system(buffer);
514 (void)sysret;
515 }
516 snprintf(buffer, sizeof(buffer), "%s.chd", chName);
517 if (!fexists(buffer))
518 return 6;
519
520 updateChannelDescription();
521
522 return 0;
523}
524
525void updateChannelDescription(void) {
526 char *command = "sddscombine *.chd -merge -pipe=out | sddsprocess -pipe=in -match=col,Name=SampleIDNumber,! -match=col,Name=Time,! allChd.sdds";
527 {
528 int sysret = system(command);
529 (void)sysret;
530 }
531}
532
533int addValue(char *spec)
534/* spec is of the form <channel>,<value> */
535/* This routine is dangerous as there is no checking to ensure that <value> is valid.
536 * Should read the .chd file, get the data type, then check for validity.
537 */
538{
539 char *ptr;
540 char buffer[BUFLEN];
541 int32_t type;
542 int64_t rows;
543 SDDS_DATASET SDDSin;
544 void *data = NULL;
545
546 if (!(ptr = strchr(spec, ',')))
547 return 1;
548 *ptr++ = 0;
549
550 snprintf(buffer, sizeof(buffer), "%s.sdds", spec);
551 if (!SDDS_InitializeAppendToPage(&SDDSin, buffer, 1, &rows)) {
552 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
553 return 2;
554 }
555 printf("Initialized, %" PRId64 " rows\n", rows);
556 fflush(stdout);
557 if (!SDDS_LengthenTable(&SDDSin, 1)) {
558 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
559 return 3;
560 }
561 printf("Lengthened table\n");
562 fflush(stdout);
563
564 if (SDDS_GetColumnInformation(&SDDSin, "type", &type, SDDS_GET_BY_NAME, spec) != SDDS_LONG) {
565 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
566 SDDS_Terminate(&SDDSin);
567 return 4;
568 }
569 printf("Got type information\n");
570 fflush(stdout);
571
572 if (type != SDDS_STRING)
573 data = SDDS_Malloc(SDDS_type_size[type - 1]);
574 if (SDDS_ScanData(ptr, type, 0, data, 0, 0) == 0) {
575 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
576 SDDS_Terminate(&SDDSin);
577 return 5;
578 }
579 printf("Scanned data\n");
580 fflush(stdout);
581
582 if (!SDDS_SetRowValues(&SDDSin, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE, rows, "SampleIDNumber", rows, "Time", getTimeInSecs(), NULL)) {
583 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
584 SDDS_Terminate(&SDDSin);
585 return 6;
586 }
587 printf("Set row values\n");
588 fflush(stdout);
589
590 switch (type) {
591 case SDDS_STRING:
592 if (!SDDS_SetRowValues(&SDDSin, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE, rows, spec, ptr, NULL)) {
593 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
594 return 7;
595 }
596 break;
597 case SDDS_FLOAT:
598 if (!SDDS_SetRowValues(&SDDSin, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE, rows, spec, *((float *)data), NULL)) {
599 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
600 return 7;
601 }
602 break;
603 case SDDS_DOUBLE:
604 if (!SDDS_SetRowValues(&SDDSin, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE, rows, spec, *((double *)data), NULL)) {
605 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
606 return 7;
607 }
608 break;
609 case SDDS_SHORT:
610 if (!SDDS_SetRowValues(&SDDSin, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE, rows, spec, *((short *)data), NULL)) {
611 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
612 return 7;
613 }
614 break;
615 case SDDS_LONG:
616 if (!SDDS_SetRowValues(&SDDSin, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE, rows, spec, *((int32_t *)data), NULL)) {
617 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
618 return 7;
619 }
620 break;
621 case SDDS_LONG64:
622 if (!SDDS_SetRowValues(&SDDSin, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE, rows, spec, *((int64_t *)data), NULL)) {
623 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
624 return 7;
625 }
626 break;
627 default:
628 break;
629 }
630 printf("Set row value\n");
631 fflush(stdout);
632 if (data)
633 free(data);
634
635 if (!SDDS_UpdatePage(&SDDSin, FLUSH_TABLE)) {
636 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
637 return 8;
638 }
639 printf("Updated page\n");
640 fflush(stdout);
641 if (!SDDS_Terminate(&SDDSin)) {
642 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
643 return 9;
644 }
645 printf("Terminated\n");
646 fflush(stdout);
647 return 0;
648}
649
650int runSddsplot(char *returnBuffer, char *options) {
651 char command[BUFLEN];
652 char template[BUFLEN];
653 int fd;
654
655 // Ensure sddsplotPath is set
656 if (!sddsplotPath) {
657 fprintf(stderr, "Error: sddsplotPath is not set.\n");
658 return 1;
659 }
660
661 // Create the template with sddsplotPath and "png-XXXXXX.png"
662 // mkstemps requires the template to end with "XXXXXX" followed by the suffix
663 snprintf(template, sizeof(template), "%s/png-XXXXXX.png", sddsplotPath);
664
665 // Create a mutable copy of the template for mkstemps
666 char *tempTemplate = strdup(template);
667 if (!tempTemplate) {
668 perror("strdup");
669 return 1;
670 }
671
672 // mkstemps replaces "XXXXXX" with a unique suffix and keeps the ".png" extension
673 fd = mkstemps(tempTemplate, 4); // 4 is the length of ".png"
674 if (fd == -1) {
675 perror("mkstemps");
676 free(tempTemplate);
677 return 1;
678 }
679
680 // Close the file descriptor as we don't need it
681 close(fd);
682
683 // tempTemplate now contains the unique filename with ".png"
684 snprintf(command, sizeof(command), "sddsplot -device=png -output=%s %s", tempTemplate, options);
685 printf("Executing: %s\n", command);
686 fflush(stdout);
687
688 // Copy the filename to returnBuffer
689 strncpy(returnBuffer, tempTemplate, BUFLEN - 1);
690 returnBuffer[BUFLEN - 1] = '\0';
691
692 // Free the duplicated template
693 free(tempTemplate);
694
695 // Execute the command
696 return system(command);
697}
698
699/*
700int runSddsplot(char *returnBuffer, char *options) {
701 char command[BUFLEN];
702 char *outputName;
703 if (!(outputName = tempnam(sddsplotPath, "png-")))
704 return 1;
705 snprintf(command, sizeof(command), "sddsplot -device=png -output=%s.png %s", outputName, options);
706 printf("Executing %s\n", command);
707 fflush(stdout);
708 strncpy(returnBuffer, outputName, BUFLEN - 1);
709 returnBuffer[BUFLEN - 1] = 0;
710 strncat(returnBuffer, ".png", BUFLEN - strlen(returnBuffer) - 1);
711 free(outputName);
712 return system(command);
713}
714*/
715
716int makeDirectoryList(int64_t *returnNumber, char ***returnBuffer) {
717 SDDS_DATASET SDDSin;
718 char command[BUFLEN];
719
720 if (returnNumber)
721 *returnNumber = 0;
722 if (returnBuffer)
723 *returnBuffer = NULL;
724
725 remove("dirList.sdds");
726 snprintf(command, sizeof(command),
727 "find . -type d -maxdepth 1 | tail -n +2 | plaindata2sdds -pipe=in dirList.sdds -input=ascii -column=DirectoryName,string -norow");
728 {
729 int sysret = system(command);
730 (void)sysret;
731 }
732 if (!SDDS_InitializeInput(&SDDSin, "dirList.sdds")) {
733 printf("Problem reading dirList.sdds\n");
734 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
735 return 1;
736 }
737 if (SDDS_ReadPage(&SDDSin) < 0)
738 /* Assume file is empty */
739 return 0;
740 if ((*returnNumber = SDDS_RowCount(&SDDSin)) < 0) {
741 printf("Row count: %" PRId64 "\n", *returnNumber);
742 return 2;
743 }
744 if (*returnNumber == 0)
745 return 0;
746 if (!(*returnBuffer = SDDS_GetColumn(&SDDSin, "DirectoryName"))) {
747 printf("Problem getting DirectoryName\n");
748 return 3;
749 }
750 return 0;
751}
752
753int getChannelList(int64_t *returnNumber, char ***returnBuffer) {
754 SDDS_DATASET SDDSin;
755
756 *returnBuffer = NULL;
757 *returnNumber = 0;
758
759 updateChannelDescription();
760 if (!fexists("allChd.sdds"))
761 return 0;
762 if (!SDDS_InitializeInput(&SDDSin, "allChd.sdds")) {
763 printf("Problem reading allChd.sdds\n");
764 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
765 return 1;
766 }
767 if (SDDS_ReadPage(&SDDSin) < 0)
768 /* Assume file is empty */
769 return 0;
770 if ((*returnNumber = SDDS_RowCount(&SDDSin)) < 0) {
771 printf("Row count: %" PRId64 "\n", *returnNumber);
772 return 2;
773 }
774 if (*returnNumber == 0)
775 return 0;
776 if (!(*returnBuffer = SDDS_GetColumn(&SDDSin, "Name"))) {
777 printf("Problem getting Name\n");
778 return 3;
779 }
780 return 0;
781}
SDDS (Self Describing Data Set) Data Types Definitions and Function Prototypes.
int32_t SDDS_ScanData(char *string, int32_t type, int32_t field_length, void *data, int64_t index, int32_t is_parameter)
Scans a string and saves the parsed value into a data pointer according to the specified data type.
int32_t SDDS_type_size[SDDS_NUM_TYPES]
Array of sizes for each supported data type.
Definition SDDS_data.c:62
int32_t SDDS_LengthenTable(SDDS_DATASET *SDDS_dataset, int64_t n_additional_rows)
int32_t SDDS_SetRowValues(SDDS_DATASET *SDDS_dataset, int32_t mode, int64_t row,...)
int32_t SDDS_StartPage(SDDS_DATASET *SDDS_dataset, int64_t expected_n_rows)
void * SDDS_GetColumn(SDDS_DATASET *SDDS_dataset, char *column_name)
Retrieves a copy of the data for a specified column, including only rows marked as "of interest".
int32_t SDDS_GetColumnInformation(SDDS_DATASET *SDDS_dataset, char *field_name, void *memory, int32_t mode,...)
Retrieves information about a specified column in the SDDS dataset.
Definition SDDS_info.c:41
int32_t SDDS_InitializeInput(SDDS_DATASET *SDDS_dataset, char *filename)
Definition SDDS_input.c:50
int32_t SDDS_Terminate(SDDS_DATASET *SDDS_dataset)
int32_t SDDS_ReadPage(SDDS_DATASET *SDDS_dataset)
int32_t SDDS_InitializeOutput(SDDS_DATASET *SDDS_dataset, int32_t data_mode, int32_t lines_per_row, const char *description, const char *contents, const char *filename)
Initializes the SDDS output dataset.
int32_t SDDS_InitializeAppendToPage(SDDS_DATASET *SDDS_dataset, const char *filename, int64_t updateInterval, int64_t *rowsPresentReturn)
Initializes the SDDS dataset for appending data to the last page of an existing file.
int32_t SDDS_DefineSimpleColumn(SDDS_DATASET *SDDS_dataset, const char *name, const char *unit, int32_t type)
Defines a simple data column within the SDDS dataset.
int32_t SDDS_UpdatePage(SDDS_DATASET *SDDS_dataset, uint32_t mode)
Updates the current page of the SDDS dataset.
int32_t SDDS_WritePage(SDDS_DATASET *SDDS_dataset)
Writes the current data table to the output file.
int32_t SDDS_DefineColumn(SDDS_DATASET *SDDS_dataset, const char *name, const char *symbol, const char *units, const char *description, const char *format_string, int32_t type, int32_t field_length)
Defines a data column within the SDDS dataset.
int32_t SDDS_WriteLayout(SDDS_DATASET *SDDS_dataset)
Writes the SDDS layout header to the output file.
void SDDS_PrintErrors(FILE *fp, int32_t mode)
Prints recorded error messages to a specified file stream.
Definition SDDS_utils.c:474
void * SDDS_Malloc(size_t size)
Allocates memory of a specified size.
Definition SDDS_utils.c:705
void SDDS_RegisterProgramName(const char *name)
Registers the executable program name for use in error messages.
Definition SDDS_utils.c:318
int32_t SDDS_IdentifyType(char *typeName)
Identifies the SDDS data type based on its string name.
#define SDDS_FLOAT
Identifier for the float data type.
Definition SDDStypes.h:43
#define SDDS_STRING
Identifier for the string data type.
Definition SDDStypes.h:85
#define SDDS_LONG
Identifier for the signed 32-bit integer data type.
Definition SDDStypes.h:61
#define SDDS_SHORT
Identifier for the signed short integer data type.
Definition SDDStypes.h:73
#define SDDS_DOUBLE
Identifier for the double data type.
Definition SDDStypes.h:37
#define SDDS_LONG64
Identifier for the signed 64-bit integer data type.
Definition SDDStypes.h:49
char * cp_str(char **s, char *t)
Copies a string, allocating memory for storage.
Definition cp_str.c:28
long fexists(const char *filename)
Checks if a file exists.
Definition fexists.c:27
double getTimeInSecs()
Get the current time in seconds since the Epoch with high resolution.
long match_string(char *string, char **option, long n_options, long mode)
Matches a given string against an array of option strings based on specified modes.
int scanargs(SCANNED_ARG **scanned, int argc, char **argv)
Definition scanargs.c:36
write(SddsFile sdds_file, output_file)
Mostly backward compatible with the PyLHC sdds module write() function.
Definition sdds.py:1967
SddsFile read(input_file)
Mostly backward compatible with the PyLHC sdds module read() function.
Definition sdds.py:1870