SDDS ToolKit Programs and Libraries for C and Python
•All Classes Files Functions Variables Macros Pages
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 chdir(rootDir);
289
290 /* Create socket */
291 sockfd = socket(AF_INET, SOCK_STREAM, 0);
292 if (sockfd < 0)
293 error("Error opening socket", argv[0]);
294 printf("sockfd = %d\n", sockfd);
295 memset(&serv_addr, 0, sizeof(serv_addr));
296
297 /* Set the socket to SO_REUSEADDR */
298 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse, sizeof(reuse)) < 0)
299 perror("setsockopt(SO_REUSEADDR) failed");
300
301#ifdef SO_REUSEPORT
302 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (const char *)&reuse, sizeof(reuse)) < 0)
303 perror("setsockopt(SO_REUSEPORT) failed");
304#endif
305
306 /* Bind the socket to a port number */
307 serv_addr.sin_family = AF_INET;
308 serv_addr.sin_addr.s_addr = INADDR_ANY;
309 serv_addr.sin_port = htons(portno);
310 if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
311 error("Error on port binding. Check port number.", argv[0]);
312
313 listen(sockfd, 5);
314 clilen = sizeof(cli_addr);
315 while (1) {
316 printf("Waiting for new socket connection\n");
317 fflush(stdout);
318 newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
319 printf("Got new socket connection\n");
320 fflush(stdout);
321 if (newsockfd < 0) {
322 if (sockfd >= 0)
323 close(sockfd);
324 error("Error on accept", argv[0]);
325 }
326 pid = fork();
327 if (pid < 0)
328 error("Error on fork", argv[0]);
329 if (pid == 0) {
330 close(sockfd);
331 dostuff(newsockfd);
332 printf("Returned from dostuff\n");
333 fflush(stdout);
334 exit(EXIT_SUCCESS);
335 } else {
336 printf("Forked process\n");
337 fflush(stdout);
338 close(newsockfd);
339 }
340 } /* end of while */
341 printf("Exited while loop\n");
342 fflush(stdout);
343 if (sockfd > 0)
344 close(sockfd);
345 return EXIT_SUCCESS; /* We never get here */
346}
347
348/******** DOSTUFF() *********************
349 There is a separate instance of this function
350 for each connection. It handles all communication
351 once a connection has been established.
352*****************************************/
353
354int dostuff(int sock) {
355 int n, code;
356 char buffer[BUFLEN];
357 char *ptr1;
358 short persist = 1;
359 int64_t nItems;
360 char **itemValue;
361
362 while (persist) {
363 memset(buffer, 0, BUFLEN);
364 n = read(sock, buffer, BUFLEN - 1);
365 if (n < 0)
366 error("ERROR reading from socket", "sddslogserver");
367 if (strlen(buffer) == 0)
368 continue;
369 chop_nl(buffer);
370 printf("Here is the message: <%s>\n", buffer);
371
372 if ((ptr1 = strchr(buffer, ':')))
373 *ptr1++ = 0;
374 if ((code = match_string(buffer, command, N_COMMANDS, EXACT_MATCH)) < 0 || forbid[code]) {
375 writeReply(sock, "Forbidden operation.", 0);
376 continue;
377 }
378 switch (code) {
379 case DISCONNECT:
380 printf("Disconnecting\n");
381 fflush(stdout);
382 persist = 0;
383 break;
384 case ADD_VALUE:
385 /* syntax: channel=value */
386 printf("Add value: %s\n", ptr1);
387 if ((code = addValue(ptr1)) == 0)
388 writeReply(sock, "ok", code);
389 else
390 writeReply(sock, "Error: Failed to add value.", code);
391 break;
392 case DELETE_VALUE:
393 /* syntax: channel,sampleID */
394 printf("Delete value: %s\n", ptr1);
395 writeReply(sock, "Error: Can't do that yet.", EXIT_FAILURE);
396 break;
397 case ADD_CHANNEL:
398 /* syntax: channel,type */
399 printf("Add channel: %s\n", ptr1);
400 if ((code = createChannel(ptr1)) == 0)
401 writeReply(sock, "ok", code);
402 else
403 writeReply(sock, "Error: Failed to create channel.", code);
404 break;
405 case MAKE_DIRECTORY:
406 /* syntax: directoryName */
407 printf("Make directory: %s\n", ptr1);
408 if ((code = mkdir(ptr1, 0700))) {
409 writeReply(sock, "Error: Making directory.", code);
410 } else {
411 writeReply(sock, "ok", code);
412 }
413 break;
414 case CHANGE_DIRECTORY:
415 /* syntax: path */
416 if (ptr1 && *ptr1 && strstr(ptr1, "..")) {
417 /* Only absolute paths are allowed */
418 writeReply(sock, "Error: Relative paths not supported.", EXIT_FAILURE);
419 } else {
420 printf("Change directory: %s\n", ptr1 && *ptr1 ? ptr1 : "base");
421 if ((code = chdirFromRoot(ptr1)))
422 writeReply(sock, "Error: CD failed.", code);
423 else
424 writeReply(sock, "CD ok.", code);
425 }
426 break;
427 case GET_TIME_SPAN:
428 /* syntax: channel,startTime,endTime */
429 printf("Get time span: %s\n", ptr1);
430 writeReply(sock, "Error: Can't do that yet.", EXIT_FAILURE);
431 break;
432 case GET_LAST_N:
433 /* syntax: channel,N; if N<=0, return all data */
434 printf("Get last N: %s\n", ptr1);
435 writeReply(sock, "Error: Can't do that yet.", EXIT_FAILURE);
436 break;
437 case SDDSPLOT:
438 /* syntax:<sddsplotCommand> */
439 printf("sddsplot: %s\n", ptr1);
440 code = runSddsplot(buffer, ptr1);
441 writeReply(sock, buffer, code);
442 break;
443 case UPDATE_CHD:
444 updateChannelDescription();
445 writeReply(sock, "ok", EXIT_SUCCESS);
446 break;
447 case LIST_DIRS:
448 if (makeDirectoryList(&nItems, &itemValue))
449 writeReply(sock, "Failed to retrieve directory list.", EXIT_FAILURE);
450 else {
451 writeReplyList(sock, nItems, itemValue);
452 freeReplyList(nItems, itemValue);
453 }
454 break;
455 case LIST_CHANNELS:
456 if (getChannelList(&nItems, &itemValue))
457 writeReply(sock, "Failed to retrieve channel list.", EXIT_FAILURE);
458 else {
459 writeReplyList(sock, nItems, itemValue);
460 freeReplyList(nItems, itemValue);
461 }
462 break;
463 default:
464 printf("Unknown command: %s\n", buffer);
465 writeReply(sock, "Error: Unknown command.", EXIT_FAILURE);
466 break;
467 }
468 }
469 return EXIT_SUCCESS;
470}
471
472int createChannel(char *spec)
473/* spec is of the form <channelName>,<type>,<units>,<description> */
474{
475 char *chName, *chType, *chUnits, *chDescription;
476 char buffer[BUFLEN];
477 SDDS_DATASET SDDSout;
478 int32_t type;
479
480 chName = spec;
481 if (!(chType = strchr(spec, ',')))
482 return 1;
483 *chType++ = 0;
484 if (!(chUnits = strchr(chType, ',')))
485 return 2;
486 *chUnits++ = 0;
487 if ((type = SDDS_IdentifyType(chType)) == 0)
488 return 2;
489 if (!(chDescription = strchr(chUnits, ',')))
490 return 3;
491 *chDescription++ = 0;
492
493 if (strcmp(chName, "Time") == 0)
494 return 4;
495
496 snprintf(buffer, sizeof(buffer), "%s.sdds", chName);
497 if (fexists(buffer))
498 return 5;
499
500 if (!SDDS_InitializeOutput(&SDDSout, SDDS_BINARY, 0, NULL, NULL, buffer) ||
501 !SDDS_DefineSimpleColumn(&SDDSout, "SampleIDNumber", NULL, SDDS_LONG64) ||
502 !SDDS_DefineSimpleColumn(&SDDSout, "Time", "s", SDDS_DOUBLE) ||
503 SDDS_DefineColumn(&SDDSout, chName, NULL, chUnits, chDescription, NULL, type, 0) < 0 ||
504 !SDDS_WriteLayout(&SDDSout) ||
505 !SDDS_StartPage(&SDDSout, 1) ||
506 !SDDS_WritePage(&SDDSout) ||
507 !SDDS_Terminate(&SDDSout))
508 return 6;
509
510 snprintf(buffer, sizeof(buffer), "sddsquery %s.sdds -sddsOutput=%s.chd -column", chName, chName);
511 system(buffer);
512 snprintf(buffer, sizeof(buffer), "%s.chd", chName);
513 if (!fexists(buffer))
514 return 6;
515
516 updateChannelDescription();
517
518 return 0;
519}
520
521void updateChannelDescription(void) {
522 char *command = "sddscombine *.chd -merge -pipe=out | sddsprocess -pipe=in -match=col,Name=SampleIDNumber,! -match=col,Name=Time,! allChd.sdds";
523 system(command);
524}
525
526int addValue(char *spec)
527/* spec is of the form <channel>,<value> */
528/* This routine is dangerous as there is no checking to ensure that <value> is valid.
529 * Should read the .chd file, get the data type, then check for validity.
530 */
531{
532 char *ptr;
533 char buffer[BUFLEN];
534 int32_t type;
535 int64_t rows;
536 SDDS_DATASET SDDSin;
537 void *data = NULL;
538
539 if (!(ptr = strchr(spec, ',')))
540 return 1;
541 *ptr++ = 0;
542
543 snprintf(buffer, sizeof(buffer), "%s.sdds", spec);
544 if (!SDDS_InitializeAppendToPage(&SDDSin, buffer, 1, &rows)) {
545 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
546 return 2;
547 }
548 printf("Initialized, %" PRId64 " rows\n", rows);
549 fflush(stdout);
550 if (!SDDS_LengthenTable(&SDDSin, 1)) {
551 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
552 return 3;
553 }
554 printf("Lengthened table\n");
555 fflush(stdout);
556
557 if (SDDS_GetColumnInformation(&SDDSin, "type", &type, SDDS_GET_BY_NAME, spec) != SDDS_LONG) {
558 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
559 SDDS_Terminate(&SDDSin);
560 return 4;
561 }
562 printf("Got type information\n");
563 fflush(stdout);
564
565 if (type != SDDS_STRING)
566 data = SDDS_Malloc(SDDS_type_size[type - 1]);
567 if (SDDS_ScanData(ptr, type, 0, data, 0, 0) == 0) {
568 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
569 SDDS_Terminate(&SDDSin);
570 return 5;
571 }
572 printf("Scanned data\n");
573 fflush(stdout);
574
575 if (!SDDS_SetRowValues(&SDDSin, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE, rows, "SampleIDNumber", rows, "Time", getTimeInSecs(), NULL)) {
576 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
577 SDDS_Terminate(&SDDSin);
578 return 6;
579 }
580 printf("Set row values\n");
581 fflush(stdout);
582
583 switch (type) {
584 case SDDS_STRING:
585 if (!SDDS_SetRowValues(&SDDSin, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE, rows, spec, ptr, NULL)) {
586 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
587 return 7;
588 }
589 break;
590 case SDDS_FLOAT:
591 if (!SDDS_SetRowValues(&SDDSin, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE, rows, spec, *((float *)data), NULL)) {
592 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
593 return 7;
594 }
595 break;
596 case SDDS_DOUBLE:
597 if (!SDDS_SetRowValues(&SDDSin, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE, rows, spec, *((double *)data), NULL)) {
598 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
599 return 7;
600 }
601 break;
602 case SDDS_SHORT:
603 if (!SDDS_SetRowValues(&SDDSin, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE, rows, spec, *((short *)data), NULL)) {
604 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
605 return 7;
606 }
607 break;
608 case SDDS_LONG:
609 if (!SDDS_SetRowValues(&SDDSin, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE, rows, spec, *((int32_t *)data), NULL)) {
610 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
611 return 7;
612 }
613 break;
614 case SDDS_LONG64:
615 if (!SDDS_SetRowValues(&SDDSin, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE, rows, spec, *((int64_t *)data), NULL)) {
616 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
617 return 7;
618 }
619 break;
620 default:
621 break;
622 }
623 printf("Set row value\n");
624 fflush(stdout);
625 if (data)
626 free(data);
627
628 if (!SDDS_UpdatePage(&SDDSin, FLUSH_TABLE)) {
629 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
630 return 8;
631 }
632 printf("Updated page\n");
633 fflush(stdout);
634 if (!SDDS_Terminate(&SDDSin)) {
635 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
636 return 9;
637 }
638 printf("Terminated\n");
639 fflush(stdout);
640 return 0;
641}
642
643int runSddsplot(char *returnBuffer, char *options) {
644 char command[BUFLEN];
645 char template[BUFLEN];
646 int fd;
647
648 // Ensure sddsplotPath is set
649 if (!sddsplotPath) {
650 fprintf(stderr, "Error: sddsplotPath is not set.\n");
651 return 1;
652 }
653
654 // Create the template with sddsplotPath and "png-XXXXXX.png"
655 // mkstemps requires the template to end with "XXXXXX" followed by the suffix
656 snprintf(template, sizeof(template), "%s/png-XXXXXX.png", sddsplotPath);
657
658 // Create a mutable copy of the template for mkstemps
659 char *tempTemplate = strdup(template);
660 if (!tempTemplate) {
661 perror("strdup");
662 return 1;
663 }
664
665 // mkstemps replaces "XXXXXX" with a unique suffix and keeps the ".png" extension
666 fd = mkstemps(tempTemplate, 4); // 4 is the length of ".png"
667 if (fd == -1) {
668 perror("mkstemps");
669 free(tempTemplate);
670 return 1;
671 }
672
673 // Close the file descriptor as we don't need it
674 close(fd);
675
676 // tempTemplate now contains the unique filename with ".png"
677 snprintf(command, sizeof(command), "sddsplot -device=png -output=%s %s", tempTemplate, options);
678 printf("Executing: %s\n", command);
679 fflush(stdout);
680
681 // Copy the filename to returnBuffer
682 strncpy(returnBuffer, tempTemplate, BUFLEN - 1);
683 returnBuffer[BUFLEN - 1] = '\0';
684
685 // Free the duplicated template
686 free(tempTemplate);
687
688 // Execute the command
689 return system(command);
690}
691
692/*
693int runSddsplot(char *returnBuffer, char *options) {
694 char command[BUFLEN];
695 char *outputName;
696 if (!(outputName = tempnam(sddsplotPath, "png-")))
697 return 1;
698 snprintf(command, sizeof(command), "sddsplot -device=png -output=%s.png %s", outputName, options);
699 printf("Executing %s\n", command);
700 fflush(stdout);
701 strncpy(returnBuffer, outputName, BUFLEN - 1);
702 returnBuffer[BUFLEN - 1] = 0;
703 strncat(returnBuffer, ".png", BUFLEN - strlen(returnBuffer) - 1);
704 free(outputName);
705 return system(command);
706}
707*/
708
709int makeDirectoryList(int64_t *returnNumber, char ***returnBuffer) {
710 SDDS_DATASET SDDSin;
711 char command[BUFLEN];
712
713 if (returnNumber)
714 *returnNumber = 0;
715 if (returnBuffer)
716 *returnBuffer = NULL;
717
718 remove("dirList.sdds");
719 snprintf(command, sizeof(command),
720 "find . -type d -maxdepth 1 | tail -n +2 | plaindata2sdds -pipe=in dirList.sdds -input=ascii -column=DirectoryName,string -norow");
721 system(command);
722 if (!SDDS_InitializeInput(&SDDSin, "dirList.sdds")) {
723 printf("Problem reading dirList.sdds\n");
724 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
725 return 1;
726 }
727 if (SDDS_ReadPage(&SDDSin) < 0)
728 /* Assume file is empty */
729 return 0;
730 if ((*returnNumber = SDDS_RowCount(&SDDSin)) < 0) {
731 printf("Row count: %" PRId64 "\n", *returnNumber);
732 return 2;
733 }
734 if (*returnNumber == 0)
735 return 0;
736 if (!(*returnBuffer = SDDS_GetColumn(&SDDSin, "DirectoryName"))) {
737 printf("Problem getting DirectoryName\n");
738 return 3;
739 }
740 return 0;
741}
742
743int getChannelList(int64_t *returnNumber, char ***returnBuffer) {
744 SDDS_DATASET SDDSin;
745
746 *returnBuffer = NULL;
747 *returnNumber = 0;
748
749 updateChannelDescription();
750 if (!fexists("allChd.sdds"))
751 return 0;
752 if (!SDDS_InitializeInput(&SDDSin, "allChd.sdds")) {
753 printf("Problem reading allChd.sdds\n");
754 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
755 return 1;
756 }
757 if (SDDS_ReadPage(&SDDSin) < 0)
758 /* Assume file is empty */
759 return 0;
760 if ((*returnNumber = SDDS_RowCount(&SDDSin)) < 0) {
761 printf("Row count: %" PRId64 "\n", *returnNumber);
762 return 2;
763 }
764 if (*returnNumber == 0)
765 return 0;
766 if (!(*returnBuffer = SDDS_GetColumn(&SDDSin, "Name"))) {
767 printf("Problem getting Name\n");
768 return 3;
769 }
770 return 0;
771}
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:49
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:432
void * SDDS_Malloc(size_t size)
Allocates memory of a specified size.
Definition SDDS_utils.c:639
void SDDS_RegisterProgramName(const char *name)
Registers the executable program name for use in error messages.
Definition SDDS_utils.c:288
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