Libtrap: Internal development docs  1.16.1
ifc_file.c
Go to the documentation of this file.
1 /**
2  * \file ifc_file.c
3  * \brief TRAP file interfaces
4  * \author Tomas Jansky <janskto1@fit.cvut.cz>
5  * \date 2015
6  */
7 /*
8  * Copyright (C) 2015, 2016 CESNET
9  *
10  * LICENSE TERMS
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  * notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  * notice, this list of conditions and the following disclaimer in
19  * the documentation and/or other materials provided with the
20  * distribution.
21  * 3. Neither the name of the Company nor the names of its contributors
22  * may be used to endorse or promote products derived from this
23  * software without specific prior written permission.
24  *
25  * ALTERNATIVELY, provided that this notice is retained in full, this
26  * product may be distributed under the terms of the GNU General Public
27  * License (GPL) version 2 or later, in which case the provisions
28  * of the GPL apply INSTEAD OF those given above.
29  *
30  * This software is provided ``as is'', and any express or implied
31  * warranties, including, but not limited to, the implied warranties of
32  * merchantability and fitness for a particular purpose are disclaimed.
33  * In no event shall the company or contributors be liable for any
34  * direct, indirect, incidental, special, exemplary, or consequential
35  * damages (including, but not limited to, procurement of substitute
36  * goods or services; loss of use, data, or profits; or business
37  * interruption) however caused and on any theory of liability, whether
38  * in contract, strict liability, or tort (including negligence or
39  * otherwise) arising in any way out of the use of this software, even
40  * if advised of the possibility of such damage.
41  *
42  */
43 #define _GNU_SOURCE
44 #include <string.h>
45 #include <stdlib.h>
46 #include <stdint.h>
47 #include <stdio.h>
48 #include <inttypes.h>
49 #include <arpa/inet.h>
50 #include <wordexp.h>
51 #include <unistd.h>
52 #include <sys/stat.h>
53 #include <errno.h>
54 
55 #include "../include/libtrap/trap.h"
56 #include "trap_ifc.h"
57 #include "trap_internal.h"
58 #include "trap_error.h"
59 #include "ifc_file.h"
60 
61 /**
62  * \addtogroup trap_ifc TRAP communication module interface
63  * @{
64  */
65 /**
66  * \addtogroup file_ifc file interface module
67  * @{
68  */
69 
70 /**
71  * \brief Close file and free allocated memory.
72  * \param[in] priv pointer to module private data
73  */
74 void file_destroy(void *priv)
75 {
76  int i;
77  file_private_t *config = (file_private_t*) priv;
78 
79  if (config) {
80  if (config->file_cnt != 0) {
81  for (i = 0; i < config->file_cnt; i++) {
82  free(config->files[i]);
83  }
84 
85  free(config->files);
86  }
87 
88  if (config->fd) {
89  fclose(config->fd);
90  }
91 
92  if (config->buffer.header) {
93  free(config->buffer.header);
94  }
95 
96  free(config);
97  } else {
98  VERBOSE(CL_ERROR, "FILE IFC: attempt to destroy IFC that is probably not initialized.");
99  }
100 }
101 
102 /**
103  * \brief Set interface state as terminated.
104  * \param[in] priv pointer to module private data
105  */
106 void file_terminate(void *priv)
107 {
108  if (priv) {
109  ((file_private_t*)priv)->is_terminated = 1;
110  } else {
111  VERBOSE(CL_ERROR, "FILE IFC: attempt to terminate IFC that is probably not initialized.");
112  }
113 }
114 
115 /**
116  * Author Jonathon Reinhart
117  * Adapted from https://gist.github.com/JonathonReinhart/8c0d90191c38af2dcadb102c4e202950
118  * \brief Create path, recursive.
119  * \param[in] path where file will be created
120  * \return 0 on success, -1 otherwise
121  */
122 int _mkdir(const char *path)
123 {
124  mode_t perm = S_IRWXU | S_IXGRP | S_IRGRP | S_IROTH | S_IXOTH;
125  const size_t len = strlen(path);
126  char _path[PATH_MAX];
127  char *p;
128 
129  if (len > sizeof(_path) - 1) {
130  return -1;
131  }
132 
133  strcpy(_path, path);
134  for (p = _path + 1; *p; p++) {
135  if (*p == '/') {
136  /* Temporarily truncate */
137  *p = '\0';
138 
139  if (mkdir(_path, perm) != 0) {
140  if (errno != EEXIST) {
141  return -1;
142  }
143  }
144 
145  *p = '/';
146  }
147  }
148 
149  return 0;
150 }
151 
152 /**
153  * \brief Create file dump with current configuration (for debugging)
154  * \param[in] priv pointer to module private data
155  * \param[in] idx number of interface
156  * \param[in] path path where dump file will be created
157  */
158 static void file_create_dump(void *priv, uint32_t idx, const char *path)
159 {
160  int ret;
161  char *config_file = NULL;
162  FILE *fd = NULL;
163 
164  file_private_t *cf = (file_private_t*) priv;
165  ret = asprintf(&config_file, "%s/trap-i%02"PRIu32"-config.txt", path, idx);
166  if (ret == -1) {
167  VERBOSE(CL_ERROR, "FILE IFC: not enough memory, dump failed. (%s:%d)", __FILE__, __LINE__);
168  return;
169  }
170 
171  fd = fopen(config_file, "w");
172  if (fd == NULL) {
173  free(config_file);
174  VERBOSE(CL_ERROR, "FILE IFC: unable to write to dump file. (%s:%d)", __FILE__, __LINE__);
175  return;
176  }
177 
178  fprintf(fd, "Filename: %s\nMode: %s\nTerminated status: %c\n", cf->filename, cf->mode, cf->is_terminated);
179  fclose(fd);
180  free(config_file);
181 }
182 
183 /**
184  * \brief Create a new path and filename from the template created during interface initialization.
185  * New filename is stored in file_private_t->filename.
186  *
187  * \param[in,out] config Pointer to module private data.
188  *
189  * \return TRAP_E_OK on success,
190  TRAP_E_MEMORY if function time(NULL) returns -1,
191  TRAP_E_IO_ERROR if error occurs during directory creation,
192  TRAP_E_BADPARAMS if the specified path and filename exceeds MAX_PATH - 1 bytes.
193  */
195 {
196  char buf[PATH_MAX];
197  char suffix[FILE_SIZE_SUFFIX_LEN + 1];
198  uint8_t valid_suffix_present = 0;
199 
200  config->create_time = time(NULL);
201  if (config->create_time == -1) {
202  VERBOSE(CL_ERROR, "FILE IFC[%"PRIu32"]: Unable to retrieve current timestamp.", config->ifc_idx);
203  return TRAP_E_MEMORY;
204  }
205 
206  /* Get actual time a round the time based on user specified parameter */
207  if (config->file_change_time > 0) {
208  config->create_time -= (config->create_time % (config->file_change_time * 60));
209  }
210 
211  /* Create valid path string based on the template and actual time */
212  size_t len = strftime(buf, PATH_MAX - FILE_SIZE_SUFFIX_LEN, config->filename_tmplt, localtime(&config->create_time));
213  if (len == 0) {
214  VERBOSE(CL_ERROR, "FILE IFC[%"PRIu32"]: Path and filename exceeds maximum size: %u.", config->ifc_idx, PATH_MAX - FILE_SIZE_SUFFIX_LEN);
215  return TRAP_E_BADPARAMS;
216  }
217 
218  /* Recursively create specified directory and subdirectories*/
219  if (_mkdir(buf) != 0) {
220  VERBOSE(CL_ERROR, "FILE IFC[%"PRIu32"]: Unable to create specified directory.", config->ifc_idx);
221  return TRAP_E_IO_ERROR;
222  }
223 
224  /* If the user specified append mode, get the lowest possible numeric suffix for which there does not exist a file */
225  if (config->mode[0] == 'a') {
226  while (42) {
227  if (sprintf(suffix, ".%05" PRIu16, config->file_index) < 0) {
228  VERBOSE(CL_ERROR, "FILE IFC[%"PRIu32"]: sprintf failed.", config->ifc_idx);
229  return TRAP_E_IO_ERROR;
230  }
231 
232  memcpy(buf + len, suffix, FILE_SIZE_SUFFIX_LEN);
233  buf[len + FILE_SIZE_SUFFIX_LEN] = 0;
234  config->file_index++;
235 
236  /* Detected overflow */
237  if (config->file_index == 0) {
238  VERBOSE(CL_ERROR, "FILE IFC[%"PRIu32"]: No valid file names left.", config->ifc_idx);
239  return TRAP_E_IO_ERROR;
240  }
241 
242  if (access(buf, F_OK) != 0) {
243  len += FILE_SIZE_SUFFIX_LEN;
244  valid_suffix_present = 1;
245  break;
246  }
247  }
248  }
249 
250  /* If the user specified file splitting based on size (and suffix was not yet added due to 'append' mode) */
251  if (config->file_change_size != 0 && !valid_suffix_present) {
252  if (sprintf(suffix, ".%05" PRIu16, config->file_index) < 0) {
253  VERBOSE(CL_ERROR, "FILE IFC[%"PRIu32"]: sprintf failed.", config->ifc_idx);
254  return TRAP_E_IO_ERROR;
255  }
256 
257  memcpy(buf + len, suffix, FILE_SIZE_SUFFIX_LEN);
258  len += FILE_SIZE_SUFFIX_LEN;
259  buf[len] = 0;
260  config->file_index++;
261  }
262 
263  /* Copy newly created path to context inner data structure */
264  strncpy(config->filename, buf, len);
265  return TRAP_E_OK;
266 }
267 
268 /**
269  * \brief Close previous file, open next file (name taken in file_private_t->filename).
270  * Negotiation must be performed after changing the file.
271  *
272  * \param[in,out] c Pointer to module private data.
273  *
274  * \return TRAP_E_OK on success,
275  * TRAP_E_BADPARAMS if the next file cannot be opened.
276  */
278 {
279  if (c->fd != NULL) {
280  fclose(c->fd);
281  c->fd = NULL;
282  }
283 
284  c->neg_initialized = 0;
285  c->fd = fopen(c->filename, c->mode);
286  if (c->fd == NULL) {
287  VERBOSE(CL_ERROR, "FILE IFC[%"PRIu32"]: unable to open file \"%s\" in mode \"%c\". Possible reasons: non-existing file, bad permission, file can not be opened in this mode.", c->ifc_idx, c->filename, c->mode[0]);
288  return TRAP_E_BADPARAMS;
289  }
290 
291  return TRAP_E_OK;
292 }
293 
294 /***** Receiver *****/
295 
296 /**
297  * \addtogroup file_receiver
298  * @{
299  */
300 
301 /**
302  * \brief Read data from a file.
303  * \param[in] priv pointer to module private data
304  * \param[out] data pointer to a memory block in which data is to be stored
305  * \param[out] size pointer to a memory block in which size of read data is to be stored
306  * \param[in] timeout NOT USED IN THIS INTERFACE
307  * \return 0 on success (TRAP_E_OK), TRAP_E_IO_ERROR if error occurs during reading, TRAP_E_TERMINATED if interface was terminated.
308  */
309 int file_recv(void *priv, void *data, uint32_t *size, int timeout)
310 {
311  size_t loaded;
312  /* Header of message inside the buffer */
313  uint16_t *m_head = data;
314  uint32_t data_size = 0;
315 
316  file_private_t *config = (file_private_t*) priv;
317 
318  if (config->is_terminated) {
319  return trap_error(config->ctx, TRAP_E_TERMINATED);
320  }
321 
322  /* Check whether the file stream is open */
323  if (config->fd == NULL) {
324  return trap_error(config->ctx, TRAP_E_NOT_INITIALIZED);
325  }
326 
327 #ifdef ENABLE_NEGOTIATION
328 neg_start:
329  if (config->neg_initialized == 0) {
330  switch(input_ifc_negotiation((void *) config, TRAP_IFC_TYPE_FILE)) {
331  case NEG_RES_FMT_UNKNOWN:
332  VERBOSE(CL_VERBOSE_LIBRARY, "FILE INPUT IFC[%"PRIu32"] negotiation result: failed (unknown data format of the output interface).", config->ifc_idx);
334 
335  case NEG_RES_CONT:
336  VERBOSE(CL_VERBOSE_LIBRARY, "FILE INPUT IFC[%"PRIu32"] negotiation result: success.", config->ifc_idx);
337  config->neg_initialized = 1;
338  break;
339 
341  VERBOSE(CL_VERBOSE_LIBRARY, "FILE INPUT IFC[%"PRIu32"] negotiation result: success (data specifier of the input interface is subset of the output interface data specifier).", config->ifc_idx);
342  config->neg_initialized = 1;
343  break;
344 
346  VERBOSE(CL_VERBOSE_LIBRARY, "FILE INPUT IFC[%"PRIu32"] negotiation result: success (new data specifier of the output interface is subset of the old one; it was not first negotiation).", config->ifc_idx);
347  config->neg_initialized = 1;
348  break;
349 
350  case NEG_RES_FAILED:
351  VERBOSE(CL_VERBOSE_LIBRARY, "FILE INPUT IFC[%"PRIu32"] negotiation result: failed (error while receiving hello message from output interface).", config->ifc_idx);
353 
355  VERBOSE(CL_VERBOSE_LIBRARY, "FILE INPUT IFC[%"PRIu32"] negotiation result: failed (data format or data specifier mismatch).", config->ifc_idx);
356  return TRAP_E_FORMAT_MISMATCH;
357 
358  default:
359  VERBOSE(CL_VERBOSE_LIBRARY, "FILE INPUT IFC[%"PRIu32"] negotiation result: default case.", config->ifc_idx);
360  break;
361  }
362  }
363 #endif
364 
365  /* Read 4 bytes from the file, determining the length of bytes to be read to @param[out] data */
366  loaded = fread(&data_size, sizeof(uint32_t), 1, config->fd);
367  if (loaded != 1) {
368  if (feof(config->fd)) {
369 
370  /* Test whether this was the last file */
371  if (++(config->file_index) >= config->file_cnt) {
372  /* Set size of buffer to the size of 1 message (including its header) */
373  (*size) = 2;
374  /* Set the header of message to 0B */
375  *m_head = 0;
376 
377  return TRAP_E_OK;
378  }
379 
380  strncpy(config->filename, config->files[config->file_index], sizeof(config->filename) - 1);
381  if (switch_file(config) == TRAP_E_OK) {
382 #ifdef ENABLE_NEGOTIATION
383  goto neg_start;
384 #endif
385  } else {
386  return trap_errorf(config->ctx, TRAP_E_IO_ERROR, "INPUT FILE IFC[%"PRIu32"]: Unable to open next file.", config->ifc_idx);
387  }
388  } else {
389  VERBOSE(CL_ERROR, "INPUT FILE IFC[%"PRIu32"]: Read error occurred in file: %s", config->ifc_idx, config->filename);
390  return trap_errorf(config->ctx, TRAP_E_IO_ERROR, "INPUT FILE IFC[%"PRIu32"]: Unable to read.", config->ifc_idx);
391  }
392  }
393 
394  *size = ntohl(data_size);
395  /* Read (*size) bytes from the file */
396  loaded = fread(data, 1, (*size), config->fd);
397  if (loaded != (*size)) {
398  VERBOSE(CL_ERROR, "INPUT FILE IFC[%"PRIu32"]: Read incorrect number of bytes from file: %s. Attempted to read %d bytes, but the actual count of bytes read was %zu.", config->ifc_idx, config->filename, (*size), loaded);
399  }
400 
401  return TRAP_E_OK;
402 }
403 
404 char *file_recv_ifc_get_id(void *priv)
405 {
406  return ((priv) ? ((file_private_t *) priv)->filename : NULL);
407 }
408 
409 uint8_t file_recv_ifc_is_conn(void *priv)
410 {
411  if (priv == NULL) {
412  return 0;
413  }
414  file_private_t *config = (file_private_t *) priv;
415  if (config->fd != NULL) {
416  return 1;
417  }
418  return 0;
419 }
420 
421 /**
422  * \brief Allocate and initiate file input interface.
423  * This function is called by TRAP library to initialize one input interface.
424  *
425  * \param[in,out] ctx Pointer to the private libtrap context data (trap_ctx_init()).
426  * \param[in] params Configuration string containing *file_name*,
427  * where file_name is a path to a file from which data is to be read
428  * \param[in,out] ifc IFC interface used for calling file module.
429  * \param[in] idx Index of IFC that is created.
430  * \return 0 on success (TRAP_E_OK), TRAP_E_MEMORY, TRAP_E_BADPARAMS on error
431  */
432 int create_file_recv_ifc(trap_ctx_priv_t *ctx, const char *params, trap_input_ifc_t *ifc, uint32_t idx)
433 {
434  file_private_t *priv;
435  size_t name_length;
436  wordexp_t files_exp;
437  int i, j;
438 
439  if (params == NULL) {
440  return trap_errorf(ctx, TRAP_E_BADPARAMS, "FILE INPUT IFC[%"PRIu32"]: Parameter is null pointer.", idx);
441  }
442 
443  /* Create structure to store private data */
444  priv = calloc(1, sizeof(file_private_t));
445  if (!priv) {
446  return trap_error(ctx, TRAP_E_MEMORY);
447  }
448 
449  priv->ctx = ctx;
450  priv->ifc_idx = idx;
451  /* Perform shell-like expansion of ~ */
452  if (wordexp(params, &files_exp, 0) != 0) {
453  VERBOSE(CL_ERROR, "FILE INPUT IFC[%"PRIu32"]: Unable to perform shell-like expansion of: %s", idx, params);
454  free(priv);
455  return trap_errorf(ctx, TRAP_E_BADPARAMS, "FILE INPUT IFC[%"PRIu32"]: Unable to perform shell-like expansion.", idx);
456  }
457 
458  if (files_exp.we_wordc == 0) {
459  VERBOSE(CL_ERROR, "FILE INPUT IFC[%"PRIu32"]: No files found for parameter: '%s'", idx, params);
460  free(priv);
461  return trap_errorf(ctx, TRAP_E_BADPARAMS, "FILE INPUT IFC[%"PRIu32"]: Unable to perform shell-like expansion.", idx);
462  }
463 
464  priv->file_cnt = files_exp.we_wordc;
465  priv->files = (char**) calloc(priv->file_cnt, sizeof(char*));
466  if (!priv->files) {
467  free(priv);
468  wordfree(&files_exp);
469  return trap_error(ctx, TRAP_E_MEMORY);
470  }
471 
472  for (i = 0; i < priv->file_cnt; i++) {
473  name_length = strlen(files_exp.we_wordv[i]);
474  priv->files[i] = (char*) calloc(name_length + 1, sizeof(char));
475  if (!priv->files[i]) {
476  for (j = i - 1; j >= 0; j --) {
477  free(priv->files[j]);
478  }
479 
480  free(priv->files);
481  free(priv);
482  wordfree(&files_exp);
483  return trap_error(ctx, TRAP_E_MEMORY);
484  }
485 
486  strncpy(priv->files[i], files_exp.we_wordv[i], name_length);
487  }
488 
489  wordfree(&files_exp);
490 
491  /* Check if the expanded path is not longer than supported maximum path length. */
492  if (strlen(priv->files[0]) > PATH_MAX - 1) {
493  VERBOSE(CL_ERROR, "FILE INPUT IFC[%"PRIu32"]: Path and filename exceeds maximum size: %u.", idx, PATH_MAX - 1);
494  for (i = 0; i < priv->file_cnt; i++) {
495  free(priv->files[i]);
496  }
497 
498  free(priv->files);
499  free(priv);
500  return trap_errorf(ctx, TRAP_E_BADPARAMS, "FILE INPUT IFC[%"PRIu32"]: Path and filename exceeds maximum size.", idx);
501  }
502 
503  strncpy(priv->filename, priv->files[0], PATH_MAX - 1);
504 
505  /* Sets mode and filename */
506  strcpy(priv->mode, "rb");
507 
508  /* Attempts to open the file */
509  priv->fd = fopen(priv->filename, priv->mode);
510  if (priv->fd == NULL) {
511  VERBOSE(CL_ERROR, "INPUT FILE IFC[%"PRIu32"]: unable to open file \"%s\". Possible reasons: non-existing file, bad permission.", idx, priv->filename);
512  for (i = 0; i < priv->file_cnt; i++) {
513  free(priv->files[i]);
514  }
515 
516  free(priv->files);
517  free(priv);
518  return trap_errorf(ctx, TRAP_E_BADPARAMS, "INPUT FILE IFC[%"PRIu32"]: Unable to open file.", idx);
519  }
520 
521  /* Fills interface structure */
522  ifc->recv = file_recv;
523  ifc->terminate = file_terminate;
524  ifc->destroy = file_destroy;
526  ifc->priv = priv;
529 
530  return TRAP_E_OK;
531 }
532 
533 /**
534  * @}
535  *//* file_receiver */
536 
537 /***** Sender *****/
538 
539 void switch_file_wrapper(void *priv)
540 {
541  file_private_t *c = (file_private_t *) priv;
542  if (c && !c->is_terminated && (create_next_filename(c) == TRAP_E_OK)) {
543  if (switch_file(c) != TRAP_E_OK) {
544  VERBOSE(CL_WARNING, "disconnect_clients sub function switch_file failed.");
545  }
546  }
547 }
548 /**
549  * \addtogroup file_sender
550  * @{
551  */
552 
553 /**
554  * \brief Write data to a file.
555  * Data to write are expected as a trap_buffer_header_t structure, thus actual length of data to be written is determined from trap_buffer_header_t->data_length
556  * trap_buffer_header_t->data_length is expected to be in network byte order (little endian)
557  *
558  * \param[in] priv pointer to module private data
559  * \param[in] data pointer to data to write
560  * \param[in] size size of data to write - NOT USED IN THIS INTERFACE
561  * \param[in] timeout NOT USED IN THIS INTERFACE
562  * \return 0 on success (TRAP_E_OK), TRAP_E_IO_ERROR if error occurs during writing, TRAP_E_TERMINATED if interface was terminated.
563  */
564 int file_write_buffer(void *priv, const void *data, uint32_t size, int timeout)
565 {
566  int ret_val = 0;
567  file_private_t *config = (file_private_t*) priv;
568  size_t written;
569 
570  if (config->is_terminated) {
571  return trap_error(config->ctx, TRAP_E_TERMINATED);
572  }
573 
574  /* Check whether the file stream is opened */
575  if (config->fd == NULL) {
576  return trap_error(config->ctx, TRAP_E_NOT_INITIALIZED);
577  }
578 
579 #ifdef ENABLE_NEGOTIATION
580  if (config->neg_initialized == 0) {
581  ret_val = output_ifc_negotiation((void *) config, TRAP_IFC_TYPE_FILE, 0);
582  if (ret_val == NEG_RES_OK) {
583  VERBOSE(CL_VERBOSE_LIBRARY, "FILE OUTPUT IFC[%"PRIu32"] negotiation result: success.", config->ifc_idx);
584  config->neg_initialized = 1;
585  fflush(config->fd);
586  } else if (ret_val == NEG_RES_FMT_UNKNOWN) {
587  VERBOSE(CL_VERBOSE_LIBRARY, "FILE OUTPUT IFC[%"PRIu32"] negotiation result: failed (unknown data format of this output interface -> refuse client).", config->ifc_idx);
588  return trap_error(config->ctx, TRAP_E_NOT_INITIALIZED);
589  } else { /* ret_val == NEG_RES_FAILED */
590  VERBOSE(CL_VERBOSE_LIBRARY, "FILE OUTPUT IFC[%"PRIu32"] negotiation result: failed (error while sending hello message to input interface).", config->ifc_idx);
591  return trap_error(config->ctx, TRAP_E_NOT_INITIALIZED);
592  }
593  }
594 #endif
595 
596  /* Writes data_length bytes to the file */
597  written = fwrite(data, 1, size, config->fd);
598  if (written != size) {
599  return trap_errorf(config->ctx, TRAP_E_IO_ERROR, "FILE OUTPUT IFC[%"PRIu32"]: unable to write to file: %s", config->ifc_idx, config->filename);
600  }
601 
602  if (config->file_change_time != 0) {
603  time_t current_time = time(NULL);
604 
605  /* Check whether new file should be created */
606  if (difftime(current_time, config->create_time) / 60 >= config->file_change_time) {
607  config->file_index = 0;
608  /* Create new filename from the current timestamp */
609  int status = create_next_filename(config);
610  if (status != TRAP_E_OK) {
611  return trap_errorf(config->ctx, status, "FILE OUTPUT IFC[%"PRIu32"]: Error during output file creation.", config->ifc_idx);
612  }
613 
614  /* Open newly created file */
615  status = switch_file(config);
616  if (status != TRAP_E_OK) {
617  return trap_errorf(config->ctx, status, "FILE OUTPUT IFC[%"PRIu32"]: Error during output file opening.", config->ifc_idx);
618  }
619  }
620  }
621 
622  if (config->file_change_size != 0 && (uint64_t)ftell(config->fd) >= (uint64_t)(1024 * 1024 * (uint64_t)config->file_change_size)) {
623 
624  /* Create new filename from the current timestamp */
625  int status = create_next_filename(config);
626  if (status != TRAP_E_OK) {
627  return trap_errorf(config->ctx, status, "FILE OUTPUT IFC[%"PRIu32"]: Error during output file creation.", config->ifc_idx);
628  }
629 
630  /* Open newly created file */
631  status = switch_file(config);
632  if (status != TRAP_E_OK) {
633  return trap_errorf(config->ctx, status, "FILE OUTPUT IFC[%"PRIu32"]: Error during output file opening.", config->ifc_idx);
634  }
635  }
636 
637  return TRAP_E_OK;
638 }
639 
640 static inline void finish_buffer(file_buffer_t *buffer)
641 {
642  uint32_t header = htonl(buffer->wr_index);
643  memcpy(buffer->header, &header, sizeof(header));
644  buffer->finished = 1;
645 }
646 
647 static inline void insert_into_buffer(file_buffer_t *buffer, const void *data, uint16_t size)
648 {
649  uint16_t *msize = (uint16_t *)(buffer->data + buffer->wr_index);
650  (*msize) = htons(size);
651  memcpy((void *)(msize + 1), data, size);
652  buffer->wr_index += (size + sizeof(size));
653 }
654 
655 void file_flush(void *priv)
656 {
657  int result;
658  file_private_t *c = (file_private_t *) priv;
659  file_buffer_t *buffer = &c->buffer;
660 
661  finish_buffer(buffer);
662 
663  result = file_write_buffer(priv, buffer->header, buffer->wr_index + sizeof(buffer->wr_index), 0);
664 
665  if (result == TRAP_E_OK) {
666  __sync_add_and_fetch(&c->ctx->counter_send_buffer[c->ifc_idx], 1);
667 
668  /* Reset buffer and insert the message if it was not inserted. */
669  buffer->wr_index = 0;
670  buffer->finished = 0;
671  } else {
672  VERBOSE(CL_ERROR, "File IFC flush failed (file_write_buffer returned %i)", result);
673  }
674 }
675 
676 /**
677  * \brief Store message into buffer. Write buffer into file if full. If buffering is disabled, the message is sent to the output interface immediately.
678  *
679  * \param[in] priv pointer to module private data
680  * \param[in] data pointer to data to write
681  * \param[in] size size of data to write
682  * \param[in] timeout NOT USED IN THIS INTERFACE
683  *
684  * \return TRAP_E_OK Success.
685  * \return TRAP_E_TIMEOUT Message was not stored into buffer and the attempt should be repeated.
686  * \return TRAP_E_TERMINATED Libtrap was terminated during the process.
687  */
688 static inline int file_send(void *priv, const void *data, uint16_t size, int timeout)
689 {
690  int result = TRAP_E_OK;
691  file_private_t *c = (file_private_t *) priv;
692  file_buffer_t *buffer = &c->buffer;
693  uint32_t needed_size = size + sizeof(size);
694  uint32_t free_bytes = c->buffer_size - c->buffer.wr_index;
695  uint8_t reinsert = 0;
696 
697  /* Can we put message at least into empty buffer? In the worst case, we could end up with SEGFAULT -> rather skip with error */
698  if (needed_size > c->buffer_size) {
699  return trap_errorf(c->ctx, TRAP_E_MEMORY, "Buffer is too small for this message. Skipping...");
700  }
701 
702  /* Check whether the message can be stored into buffer. */
703  if (buffer->finished == 0) {
704  if (free_bytes >= needed_size) {
705  insert_into_buffer(buffer, data, size);
706 
707  /* If bufferswitch is 0, only 1 message is allowed to be stored in buffer */
708  if (c->ctx->out_ifc_list[c->ifc_idx].bufferswitch == 0) {
709  finish_buffer(buffer);
710  }
711  } else {
712  /* Need to send buffer first. */
713  finish_buffer(buffer);
714  reinsert = 1;
715  }
716  }
717 
718  /* Buffer ready to be sent. */
719  if (buffer->finished == 1) {
720 
721  result = file_write_buffer(priv, buffer->header, buffer->wr_index + sizeof(buffer->wr_index), timeout);
722 
723  if (result == TRAP_E_OK) {
724  __sync_add_and_fetch(&c->ctx->counter_send_buffer[c->ifc_idx], 1);
725 
726  /* Reset buffer and insert the message if it was not inserted. */
727  buffer->wr_index = 0;
728  buffer->finished = 0;
729  if (reinsert) {
730  insert_into_buffer(buffer, data, size);
731  }
732  }
733  }
734 
735  return result;
736 }
737 
738 int32_t file_get_client_count(void *priv)
739 {
740  return 1;
741 }
742 
743 int8_t file_get_client_stats_json(void *priv, json_t *client_stats_arr)
744 {
745  /* do not collect client statistics for this interface */
746  return 1;
747 }
748 
749 char *file_send_ifc_get_id(void *priv)
750 {
751  return ((priv) ? ((file_private_t *) priv)->filename : NULL);
752 }
753 
754 /**
755  * \brief Allocate and initiate file output interface.
756  * This function is called by TRAP library to initialize one output interface.
757  * \param[in,out] ctx Pointer to the private libtrap context data (trap_ctx_init()).
758  * \param[in] params Configuration string containing colon separated values of these parameters (in this exact order): *file_name*:*open_mode*,
759  * where file_name is a path to a file in which data is to be written and
760  * open_mode is either a - append or w - write, if no mode is specified, the file will be opened in append mode.
761  * \param[in,out] ifc IFC interface used for calling file module.
762  * \param[in] idx Index of IFC that is created.
763  * \return 0 on success (TRAP_E_OK), TRAP_E_MEMORY, TRAP_E_BADPARAMS on error
764  */
765 int create_file_send_ifc(trap_ctx_priv_t *ctx, const char *params, trap_output_ifc_t *ifc, uint32_t idx)
766 {
767  file_private_t *priv;
768  char *dest;
769  const char *params_next = NULL;
770  wordexp_t exp_result;
771  size_t length;
772 
773  uint32_t buffer_size = TRAP_IFC_MESSAGEQ_SIZE;
774 
775  if (params == NULL) {
776  return trap_errorf(ctx, TRAP_E_BADPARAMS, "FILE OUTPUT IFC[%"PRIu32"]: Parameter is null pointer.", idx);
777  }
778 
779  /* Create structure to store private data */
780  priv = calloc(1, sizeof(file_private_t));
781  if (!priv) {
782  return trap_error(ctx, TRAP_E_MEMORY);
783  }
784 
785  priv->ctx = ctx;
786  priv->ifc_idx = idx;
787  priv->buffer_size = buffer_size;
788  priv->buffer.header = malloc(buffer_size + sizeof(buffer_size));
789  if (priv->buffer.header == NULL) {
790  VERBOSE(CL_ERROR, "Memory allocation failed, terminating...");
791  free(priv);
792  return TRAP_E_MEMORY;
793  }
794  priv->buffer.data = priv->buffer.header + sizeof(buffer_size);
795  priv->buffer.wr_index = 0;
796  priv->buffer.finished = 0;
797  /* Set default mode */
798  strcpy(priv->mode, "wb");
799 
800  /* Parse file name */
801  length = strcspn(params, ":");
802  if (params[length] == ':') {
803  params_next = params + length + 1;
804  }
805 
806  if (length) {
807  dest = (char*) calloc(length + 1, sizeof(char));
808  if (!dest) {
809  free(priv->buffer.header);
810  free(priv);
811  return trap_error(ctx, TRAP_E_MEMORY);
812  }
813 
814  strncpy(dest, params, length);
815  } else {
816  free(priv->buffer.header);
817  free(priv);
818  return trap_errorf(ctx, TRAP_E_BADPARAMS, "FILE OUTPUT IFC[%"PRIu32"]: Filename not specified.", idx);
819  }
820 
821  /* Perform shell-like expansion of ~ */
822  if (wordexp(dest, &exp_result, 0) != 0) {
823  VERBOSE(CL_ERROR, "FILE OUTPUT IFC[%"PRIu32"]: Unable to perform shell-like expansion of: %s", idx, dest);
824  free(priv->buffer.header);
825  free(priv);
826  free(dest);
827  wordfree(&exp_result);
828  return trap_errorf(ctx, TRAP_E_BADPARAMS, "FILE OUTPUT IFC[%"PRIu32"]: Unable to perform shell-like expansion.", idx);
829  }
830 
831  free(dest);
832  strncpy(priv->filename_tmplt, exp_result.we_wordv[0], sizeof(priv->filename_tmplt) - 1);
833  wordfree(&exp_result);
834 
835  /* Set special behavior for /dev/stdout */
836  if (strncmp(priv->filename_tmplt, "/dev/stdout", 11) == 0) {
837  priv->mode[0] = 'w';
838  priv->file_change_size = 0;
839  priv->file_change_time = 0;
840  } else {
841  /* Parse remaining parameters */
842  while (params_next) {
843  length = strcspn(params_next, ":");
844  if (length > TIME_PARAM_LEN && strncmp(params_next, TIME_PARAM, TIME_PARAM_LEN) == 0) {
845  priv->file_change_time = atoi(params_next + TIME_PARAM_LEN);
846  if (strlen(priv->filename_tmplt) + TIME_FORMAT_STRING_LEN > sizeof(priv->filename_tmplt) - 1) {
847  free(priv->buffer.header);
848  free(priv);
849  return trap_errorf(ctx, TRAP_E_BADPARAMS, "FILE OUTPUT IFC[%"PRIu32"]: Path and filename exceeds maximum size: %zu.", idx, sizeof(priv->filename_tmplt) - 1);
850  }
851 
852  /* Append timestamp formate to the current template */
853  strcat(priv->filename_tmplt, TIME_FORMAT_STRING);
854  } else if (length > SIZE_PARAM_LEN && strncmp(params_next, SIZE_PARAM, SIZE_PARAM_LEN) == 0) {
855  priv->file_change_size = atoi(params_next + SIZE_PARAM_LEN);
856  } else if (length == 1 && params_next[0] == 'a') {
857  priv->mode[0] = 'a';
858  }
859 
860  if (params_next[length] == '\0') {
861  break;
862  }
863 
864  params_next = params_next + length + 1;
865  }
866  }
867 
868  /* Create first filename from the prepared template */
869  int status = create_next_filename(priv);
870  if (status != TRAP_E_OK) {
871  free(priv->buffer.header);
872  free(priv);
873  return trap_errorf(ctx, status, "FILE OUTPUT IFC[%"PRIu32"]: Error during output file creation.", idx);
874  }
875 
876  /* Open first file */
877  status = switch_file(priv);
878  if (status != TRAP_E_OK) {
879  free(priv->buffer.header);
880  free(priv);
881  return trap_errorf(ctx, status, "FILE OUTPUT IFC[%"PRIu32"]: Error during output file opening.", idx);
882  }
883 
884  /* Fills interface structure */
885  ifc->send = file_send;
886  ifc->flush = file_flush;
888  ifc->terminate = file_terminate;
889  ifc->destroy = file_destroy;
893  ifc->priv = priv;
895 
896  return TRAP_E_OK;
897 }
898 
899 /**
900  * @}
901  *//* file_sender */
902 
903 /**
904  * @}
905  *//* file_ifc module */
906 
907 /**
908  * @}
909  *//* ifc modules */
ifc_get_id_func_t get_id
Pointer to get_id function.
Definition: trap_ifc.h:182
#define TIME_FORMAT_STRING_LEN
Definition: ifc_file.h:54
#define SIZE_PARAM_LEN
Definition: ifc_file.h:52
#define TRAP_E_OK
Success, no error.
Definition: trap.h:87
int create_next_filename(file_private_t *config)
Create a new path and filename from the template created during interface initialization. New filename is stored in file_private_t->filename.
Definition: ifc_file.c:194
int32_t file_get_client_count(void *priv)
Definition: ifc_file.c:738
ifc_get_client_stats_json_func_t get_client_stats_json
Pointer to get_client_stats_json function.
Definition: trap_ifc.h:244
char * file_send_ifc_get_id(void *priv)
Definition: ifc_file.c:749
#define NEG_RES_SENDER_FMT_SUBSET
If the data format of input and output interfaces is the same and new data specifier of the output in...
Definition: trap_internal.h:76
Internal functions and macros for libtrap Verbose and debug macros from libcommlbr.
int8_t file_get_client_stats_json(void *priv, json_t *client_stats_arr)
Definition: ifc_file.c:743
int _mkdir(const char *path)
Create path, recursive.
Definition: ifc_file.c:122
#define FILE_SIZE_SUFFIX_LEN
Definition: ifc_file.h:55
#define NEG_RES_FAILED
If receiving the data from output interface fails or sending the data to input interface fails...
Definition: trap_internal.h:84
uint32_t ifc_idx
Definition: ifc_file.h:80
trap_ctx_priv_t * ctx
Definition: ifc_file.h:66
char is_terminated
Definition: ifc_file.h:73
ifc_get_id_func_t get_id
Pointer to get_id function.
Definition: trap_ifc.h:236
TRAP file interfaces.
int output_ifc_negotiation(void *ifc_priv_data, char ifc_type, uint32_t client_idx)
Definition: trap.c:2833
static void insert_into_buffer(file_buffer_t *buffer, const void *data, uint16_t size)
Definition: ifc_file.c:647
#define TRAP_E_TERMINATED
Interface was terminated during reading/writing.
Definition: trap.h:94
void switch_file_wrapper(void *priv)
Definition: ifc_file.c:539
uint8_t file_recv_ifc_is_conn(void *priv)
Definition: ifc_file.c:409
uint8_t neg_initialized
Definition: ifc_file.h:74
file_buffer_t buffer
Definition: ifc_file.h:82
uint32_t file_change_time
Definition: ifc_file.h:78
void file_terminate(void *priv)
Set interface state as terminated.
Definition: ifc_file.c:106
ifc_create_dump_func_t create_dump
Pointer to function for generating of dump.
Definition: trap_ifc.h:242
uint16_t file_cnt
Definition: ifc_file.h:76
int create_file_send_ifc(trap_ctx_priv_t *ctx, const char *params, trap_output_ifc_t *ifc, uint32_t idx)
Allocate and initiate file output interface. This function is called by TRAP library to initialize on...
Definition: ifc_file.c:765
uint8_t * data
Definition: ifc_file.h:61
#define TRAP_E_IO_ERROR
IO Error.
Definition: trap.h:93
#define VERBOSE(level, format, args...)
#define TIME_PARAM
Definition: ifc_file.h:49
static int trap_errorf(trap_ctx_priv_t *ctx, int err_num, const char *msg,...)
Definition: trap_error.h:92
uint8_t data[0]
ifc_get_client_count_func_t get_client_count
Pointer to get_client_count function.
Definition: trap_ifc.h:243
#define NEG_RES_CONT
If the data format and data specifier of input and output interface are the same (input interface can...
Definition: trap_internal.h:74
void file_destroy(void *priv)
Close file and free allocated memory.
Definition: ifc_file.c:74
ifc_disconn_clients_func_t disconn_clients
Pointer to disconnect_clients function.
Definition: trap_ifc.h:237
char filename_tmplt[FILENAME_TEMPLATE_LEN]
Definition: ifc_file.h:70
#define TIME_FORMAT_STRING
Definition: ifc_file.h:53
ifc_terminate_func_t terminate
Pointer to terminate function.
Definition: trap_ifc.h:184
uint8_t * header
Definition: ifc_file.h:60
char bufferswitch
Enable (1) or Disable (0) buffering, default is Enabled (1).
Definition: trap_ifc.h:250
int create_file_recv_ifc(trap_ctx_priv_t *ctx, const char *params, trap_input_ifc_t *ifc, uint32_t idx)
Allocate and initiate file input interface. This function is called by TRAP library to initialize one...
Definition: ifc_file.c:432
ifc_destroy_func_t destroy
Pointer to destructor function.
Definition: trap_ifc.h:185
uint32_t file_change_size
Definition: ifc_file.h:77
#define TRAP_E_NEGOTIATION_FAILED
Returned by trap_recv when negotiation of the output and input interfaces failed. ...
Definition: trap.h:102
ifc_send_func_t send
Pointer to send function.
Definition: trap_ifc.h:238
uint16_t file_index
Definition: ifc_file.h:75
char mode[3]
Definition: ifc_file.h:72
#define NEG_RES_FMT_UNKNOWN
If the output interface has not specified data format.
Definition: trap_internal.h:85
char filename[PATH_MAX]
Definition: ifc_file.h:71
ifc_recv_func_t recv
Pointer to receive function.
Definition: trap_ifc.h:183
time_t create_time
Definition: ifc_file.h:68
#define TRAP_E_FORMAT_MISMATCH
Returned by trap_recv when data format or data specifier of the output and input interfaces doesn&#39;t m...
Definition: trap.h:101
static void finish_buffer(file_buffer_t *buffer)
Definition: ifc_file.c:640
#define NEG_RES_FMT_MISMATCH
If the data format or data specifier of input and output interfaces does not match.
Definition: trap_internal.h:77
ifc_create_dump_func_t create_dump
Pointer to function for generating of dump.
Definition: trap_ifc.h:186
uint8_t finished
Definition: ifc_file.h:62
Error handling for TRAP.
int file_recv(void *priv, void *data, uint32_t *size, int timeout)
Read data from a file.
Definition: ifc_file.c:309
#define TRAP_E_MEMORY
Memory allocation error.
Definition: trap.h:104
char * file_recv_ifc_get_id(void *priv)
Definition: ifc_file.c:404
#define NEG_RES_OK
Signaling success (hello message successfully sent to input interface)
Definition: trap_internal.h:81
ifc_destroy_func_t destroy
Pointer to destructor function.
Definition: trap_ifc.h:241
int file_write_buffer(void *priv, const void *data, uint32_t size, int timeout)
Write data to a file. Data to write are expected as a trap_buffer_header_t structure, thus actual length of data to be written is determined from trap_buffer_header_t->data_length trap_buffer_header_t->data_length is expected to be in network byte order (little endian)
Definition: ifc_file.c:564
ifc_terminate_func_t terminate
Pointer to terminate function.
Definition: trap_ifc.h:240
static void file_create_dump(void *priv, uint32_t idx, const char *path)
Create file dump with current configuration (for debugging)
Definition: ifc_file.c:158
static int trap_error(trap_ctx_priv_t *ctx, int err_num)
Definition: trap_error.h:63
void file_flush(void *priv)
Definition: ifc_file.c:655
#define SIZE_PARAM
Definition: ifc_file.h:51
uint32_t buffer_size
Definition: ifc_file.h:79
#define NEG_RES_RECEIVER_FMT_SUBSET
If the data format of input and output interfaces is the same and data specifier of the input interfa...
Definition: trap_internal.h:75
ifc_flush_func_t flush
Pointer to flush function.
Definition: trap_ifc.h:239
#define TRAP_IFC_TYPE_FILE
trap_ifc_file (input&output part)
Definition: trap.h:177
char ** files
Definition: ifc_file.h:69
void * priv
Pointer to instance&#39;s private data.
Definition: trap_ifc.h:187
uint64_t * counter_send_buffer
int input_ifc_negotiation(void *ifc_priv_data, char ifc_type)
Definition: trap.c:2995
ifc_is_conn_func_t is_conn
Pointer to is_connected function.
Definition: trap_ifc.h:181
uint32_t wr_index
Definition: ifc_file.h:59
#define TRAP_E_NOT_INITIALIZED
TRAP library not initilized.
Definition: trap.h:103
void * priv
Pointer to instance&#39;s private data.
Definition: trap_ifc.h:245
trap_output_ifc_t * out_ifc_list
FILE * fd
Definition: ifc_file.h:67
Interface of TRAP interfaces.
static int file_send(void *priv, const void *data, uint16_t size, int timeout)
Store message into buffer. Write buffer into file if full. If buffering is disabled, the message is sent to the output interface immediately.
Definition: ifc_file.c:688
#define TIME_PARAM_LEN
Definition: ifc_file.h:50
int switch_file(file_private_t *c)
Close previous file, open next file (name taken in file_private_t->filename). Negotiation must be per...
Definition: ifc_file.c:277
#define TRAP_E_BADPARAMS
Bad parameters passed to interface initializer.
Definition: trap.h:90
#define TRAP_IFC_MESSAGEQ_SIZE
size of message queue used for buffering
Definition: trap.h:206