Asterisk - The Open Source Telephony Project  21.4.1
Data Structures | Functions
stored.c File Reference

Stored file operations for Stasis. More...

#include "asterisk.h"
#include "asterisk/astobj2.h"
#include "asterisk/paths.h"
#include "asterisk/stasis_app_recording.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

Go to the source code of this file.

Data Structures

struct  match_recording_data
 
struct  stasis_app_stored_recording
 

Functions

static char * find_recording (const char *dir_name, const char *file)
 Finds a recording in the given directory. More...
 
static int handle_find_recording (const char *dir_name, const char *filename, void *obj)
 
static int handle_scan_file (const char *dir_name, const char *filename, void *obj)
 
static int is_recording (const char *filename)
 
static struct stasis_app_stored_recordingrecording_alloc (void)
 Allocate a recording object.
 
static int recording_sort (const void *obj_left, const void *obj_right, int flags)
 
static int split_path (const char *path, char **dir, char **file)
 Split a path into directory and file, resolving canonical directory. More...
 
int stasis_app_stored_recording_copy (struct stasis_app_stored_recording *src_recording, const char *dst, struct stasis_app_stored_recording **dst_recording)
 Copy a recording. More...
 
int stasis_app_stored_recording_delete (struct stasis_app_stored_recording *recording)
 Delete a recording from disk. More...
 
struct ao2_containerstasis_app_stored_recording_find_all (void)
 Find all stored recordings on disk. More...
 
struct stasis_app_stored_recordingstasis_app_stored_recording_find_by_name (const char *name)
 Creates a stored recording object, with the given name. More...
 
const char * stasis_app_stored_recording_get_extension (struct stasis_app_stored_recording *recording)
 Returns the extension for this recording. More...
 
const char * stasis_app_stored_recording_get_file (struct stasis_app_stored_recording *recording)
 Returns the filename for this recording, for use with streamfile. More...
 
const char * stasis_app_stored_recording_get_filename (struct stasis_app_stored_recording *recording)
 Returns the full filename, with extension, for this recording. More...
 
struct ast_jsonstasis_app_stored_recording_to_json (struct stasis_app_stored_recording *recording)
 Convert stored recording info to JSON. More...
 
static void stored_recording_dtor (void *obj)
 

Detailed Description

Stored file operations for Stasis.

Author
David M. Lee, II dlee@.nosp@m.digi.nosp@m.um.co.nosp@m.m

Definition in file stored.c.

Function Documentation

static char* find_recording ( const char *  dir_name,
const char *  file 
)
static

Finds a recording in the given directory.

This function searches for a file with the given file name, with a registered format that matches its extension.

Parameters
dir_nameDirectory to search (absolute path).
fileFile name, without extension.
Returns
Absolute path of the recording file.
Return values
NULLif recording is not found.

Definition at line 187 of file stored.c.

References ast_file_read_dir.

189 {
190  struct match_recording_data data = {
191  .file = file,
192  .length = strlen(file),
193  .file_with_ext = NULL
194  };
195 
196  ast_file_read_dir(dir_name, handle_find_recording, &data);
197 
198  /* Note, string potentially allocated in handle_file_recording */
199  return data.file_with_ext;
#define ast_file_read_dir(dir_name, on_file, obj)
Iterate over each file in a given directory.
Definition: file.h:203
static int recording_sort ( const void *  obj_left,
const void *  obj_right,
int  flags 
)
static

< Deprecated name

< Deprecated name

< Deprecated name

< Deprecated name

< Deprecated name

< Deprecated name

Definition at line 223 of file stored.c.

226 {
227  const struct stasis_app_stored_recording *object_left = obj_left;
228  const struct stasis_app_stored_recording *object_right = obj_right;
229  const char *right_key = obj_right;
230  int cmp;
231 
232  switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
233  case OBJ_POINTER:
234  right_key = object_right->name;
235  /* Fall through */
236  case OBJ_KEY:
237  cmp = strcmp(object_left->name, right_key);
238  break;
239  case OBJ_PARTIAL_KEY:
240  /*
241  * We could also use a partial key struct containing a length
242  * so strlen() does not get called for every comparison instead.
243  */
244  cmp = strncmp(object_left->name, right_key, strlen(right_key));
245  break;
246  default:
247  /* Sort can only work on something with a full or partial key. */
248  ast_assert(0);
249  cmp = 0;
250  break;
251  }
#define OBJ_KEY
Definition: astobj2.h:1151
#define OBJ_POINTER
Definition: astobj2.h:1150
#define OBJ_PARTIAL_KEY
Definition: astobj2.h:1152
const ast_string_field name
Definition: stored.c:41
static int split_path ( const char *  path,
char **  dir,
char **  file 
)
static

Split a path into directory and file, resolving canonical directory.

The path is resolved relative to the recording directory. Both dir and file are allocated strings, which you must ast_free().

Parameters
pathPath to split.
[out]dirOutput parameter for directory portion.
[out]fileOutput parameter for the file portion.
Returns
0 on success.
Non-zero on error.

Definition at line 92 of file stored.c.

References ast_asprintf, ast_strdup, and RAII_VAR.

93 {
94  RAII_VAR(char *, relative_dir, NULL, ast_free);
95  RAII_VAR(char *, absolute_dir, NULL, ast_free);
96  RAII_VAR(char *, real_dir, NULL, ast_std_free);
97  char *last_slash;
98  const char *file_portion;
99 
100  relative_dir = ast_strdup(path);
101  if (!relative_dir) {
102  return -1;
103  }
104 
105  last_slash = strrchr(relative_dir, '/');
106  if (last_slash) {
107  *last_slash = '\0';
108  file_portion = last_slash + 1;
109  ast_asprintf(&absolute_dir, "%s/%s",
110  ast_config_AST_RECORDING_DIR, relative_dir);
111  } else {
112  /* There is no directory portion */
113  file_portion = path;
114  *relative_dir = '\0';
115  absolute_dir = ast_strdup(ast_config_AST_RECORDING_DIR);
116  }
117  if (!absolute_dir) {
118  return -1;
119  }
120 
121  real_dir = realpath(absolute_dir, NULL);
122  if (!real_dir) {
123  return -1;
124  }
125 
126  *dir = ast_strdup(real_dir); /* Dupe so we can ast_free() */
127  *file = ast_strdup(file_portion);
128  return (*dir && *file) ? 0 : -1;
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
const ast_string_field file
Definition: stored.c:41
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
int stasis_app_stored_recording_copy ( struct stasis_app_stored_recording src_recording,
const char *  dst,
struct stasis_app_stored_recording **  dst_recording 
)

Copy a recording.

Parameters
src_recordingThe recording to copy
dstThe destination of the recording to make
dst_recordingIf successful, the stored recording created as a result of the copy
Return values
0on success
Non-zeroon error

Definition at line 393 of file stored.c.

Referenced by ast_ari_recordings_copy_stored().

400 {
401  RAII_VAR(char *, full_path, NULL, ast_free);
402  char *dst_file = ast_strdupa(dst);
403  char *format;
404  char *last_slash;
405  int res;
406 
407  /* Drop the extension if specified, core will do this for us */
408  format = strrchr(dst_file, '.');
409  if (format) {
410  *format = '\0';
411  }
412 
413  /* See if any intermediary directories need to be made */
414  last_slash = strrchr(dst_file, '/');
415  if (last_slash) {
416  RAII_VAR(char *, tmp_path, NULL, ast_free);
417 
418  *last_slash = '\0';
419  if (ast_asprintf(&tmp_path, "%s/%s", ast_config_AST_RECORDING_DIR, dst_file) < 0) {
420  return -1;
421  }
422  if (ast_safe_mkdir(ast_config_AST_RECORDING_DIR,
423  tmp_path, 0777) != 0) {
424  /* errno set by ast_mkdir */
425  return -1;
426  }
427  *last_slash = '/';
428  if (ast_asprintf(&full_path, "%s/%s", ast_config_AST_RECORDING_DIR, dst_file) < 0) {
429  return -1;
430  }
431  } else {
432  /* There is no directory portion */
433  if (ast_asprintf(&full_path, "%s/%s", ast_config_AST_RECORDING_DIR, dst_file) < 0) {
434  return -1;
435  }
436  }
437 
438  ast_verb(4, "Copying recording %s to %s (format %s)\n", src_recording->file,
439  full_path, src_recording->format);
440  res = ast_filecopy(src_recording->file, full_path, src_recording->format);
441  if (!res) {
int ast_filecopy(const char *oldname, const char *newname, const char *fmt)
Copies a file.
Definition: file.c:1151
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
int ast_safe_mkdir(const char *base_path, const char *path, int mode)
Recursively create directory path, but only if it resolves within the given base_path.
Definition: utils.c:2584
const ast_string_field file
Definition: stored.c:41
const char * format
Definition: stored.c:41
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
int stasis_app_stored_recording_delete ( struct stasis_app_stored_recording recording)

Delete a recording from disk.

Parameters
recordingRecording to delete.
Returns
0 on success.
Non-zero on error.

Definition at line 443 of file stored.c.

Referenced by ast_ari_recordings_delete_stored().

450 {
struct ao2_container* stasis_app_stored_recording_find_all ( void  )

Find all stored recordings on disk.

Returns
Container of stasis_app_stored_recording objects.
Return values
NULLon error.

Definition at line 294 of file stored.c.

Referenced by ast_ari_recordings_list_stored().

298 {
299  struct ao2_container *recordings;
300  int res;
301 
304  if (!recordings) {
305  return NULL;
306  }
307 
308  res = ast_file_read_dirs(ast_config_AST_RECORDING_DIR,
309  handle_scan_file, recordings, -1);
310  if (res) {
311  ao2_ref(recordings, -1);
312  return NULL;
int ast_file_read_dirs(const char *dir_name, ast_file_on_file on_file, void *obj, int max_depth)
Recursively iterate through files and directories up to max_depth.
Definition: file.c:1274
static struct stasis_rest_handlers recordings
REST handler for /api-docs/recordings.json.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
Replace objects with duplicate keys in container.
Definition: astobj2.h:1211
static int recording_sort(const void *obj_left, const void *obj_right, int flags)
Definition: stored.c:223
#define ao2_container_alloc_rbtree(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a red-black tree container.
Definition: astobj2.h:1349
Generic container type.
struct stasis_app_stored_recording* stasis_app_stored_recording_find_by_name ( const char *  name)

Creates a stored recording object, with the given name.

Parameters
nameName of the recording.
Returns
New recording object.
Return values
NULLif recording is not found. errno is set to indicate why
  • ENOMEM - out of memeory
  • EACCES - file permissions (or recording is outside the config dir)
  • Any of the error codes for stat(), opendir(), readdir()

Definition at line 314 of file stored.c.

Referenced by ast_ari_recordings_copy_stored(), ast_ari_recordings_delete_stored(), ast_ari_recordings_get_stored(), and ast_ari_recordings_get_stored_file().

320 {
321  RAII_VAR(struct stasis_app_stored_recording *, recording, NULL,
322  ao2_cleanup);
323  RAII_VAR(char *, dir, NULL, ast_free);
324  RAII_VAR(char *, file, NULL, ast_free);
325  RAII_VAR(char *, file_with_ext, NULL, ast_free);
326  int res;
327  struct stat file_stat;
328  int prefix_len = strlen(ast_config_AST_RECORDING_DIR);
329 
330  errno = 0;
331 
332  if (!name) {
333  errno = EINVAL;
334  return NULL;
335  }
336 
337  recording = recording_alloc();
338  if (!recording) {
339  return NULL;
340  }
341 
342  res = split_path(name, &dir, &file);
343  if (res != 0) {
344  return NULL;
345  }
346  ast_string_field_build(recording, file, "%s/%s", dir, file);
347 
348  if (!ast_begins_with(dir, ast_config_AST_RECORDING_DIR)) {
349  /* It's possible that one or more component of the recording path is
350  * a symbolic link, this would prevent dir from ever matching. */
351  char *real_basedir = realpath(ast_config_AST_RECORDING_DIR, NULL);
352 
353  if (!real_basedir || !ast_begins_with(dir, real_basedir)) {
354  /* Attempt to escape the recording directory */
355  ast_log(LOG_WARNING, "Attempt to access invalid recording directory %s\n",
356  dir);
357  ast_std_free(real_basedir);
358  errno = EACCES;
359 
360  return NULL;
361  }
362 
363  prefix_len = strlen(real_basedir);
364  ast_std_free(real_basedir);
365  }
366 
367  /* The actual name of the recording is file with the config dir
368  * prefix removed.
369  */
370  ast_string_field_set(recording, name, recording->file + prefix_len + 1);
371 
372  file_with_ext = find_recording(dir, file);
373  if (!file_with_ext) {
374  return NULL;
375  }
376  ast_string_field_set(recording, file_with_ext, file_with_ext);
377  recording->format = strrchr(recording->file_with_ext, '.');
378  if (!recording->format) {
379  return NULL;
380  }
381  ++(recording->format);
382 
383  res = stat(file_with_ext, &file_stat);
384  if (res != 0) {
385  return NULL;
386  }
387 
388  if (!S_ISREG(file_stat.st_mode)) {
389  /* Let's not play if it's not a regular file */
390  errno = EACCES;
391  return NULL;
static char * find_recording(const char *dir_name, const char *file)
Finds a recording in the given directory.
Definition: stored.c:187
static struct stasis_app_stored_recording * recording_alloc(void)
Allocate a recording object.
Definition: stored.c:204
static int split_path(const char *path, char **dir, char **file)
Split a path into directory and file, resolving canonical directory.
Definition: stored.c:92
#define ast_string_field_build(x, field, fmt, args...)
Set a field to a complex (built) value.
Definition: stringfields.h:555
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
Definition: strings.h:97
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
const char* stasis_app_stored_recording_get_extension ( struct stasis_app_stored_recording recording)

Returns the extension for this recording.

Since
14.0.0
Parameters
recordingRecording to query.
Returns
The extension associated with this recording.
Return values
NULLon error

Definition at line 71 of file stored.c.

References stasis_app_stored_recording::format.

Referenced by ast_ari_recordings_get_stored_file().

73 {
74  if (!recording) {
75  return NULL;
76  }
77  return recording->format;
78 }
const char * format
Definition: stored.c:41
const char* stasis_app_stored_recording_get_file ( struct stasis_app_stored_recording recording)

Returns the filename for this recording, for use with streamfile.

The returned string will be valid until the recording object is freed.

Parameters
recordingRecording to query.
Returns
Absolute path to the recording file, without the extension.
Return values
NULLon error.

Definition at line 53 of file stored.c.

References stasis_app_stored_recording::file.

55 {
56  if (!recording) {
57  return NULL;
58  }
59  return recording->file;
60 }
const ast_string_field file
Definition: stored.c:41
const char* stasis_app_stored_recording_get_filename ( struct stasis_app_stored_recording recording)

Returns the full filename, with extension, for this recording.

Since
14.0.0
Parameters
recordingRecording to query.
Returns
Absolute path to the recording file, with the extension.
Return values
NULLon error

Definition at line 62 of file stored.c.

References stasis_app_stored_recording::file_with_ext.

Referenced by ast_ari_recordings_get_stored_file().

64 {
65  if (!recording) {
66  return NULL;
67  }
68  return recording->file_with_ext;
69 }
const ast_string_field file_with_ext
Definition: stored.c:41
struct ast_json* stasis_app_stored_recording_to_json ( struct stasis_app_stored_recording recording)

Convert stored recording info to JSON.

Parameters
recordingRecording to convert.
Returns
JSON representation.
Return values
NULLon error.

Definition at line 450 of file stored.c.

References stasis_app_stored_recording::file_with_ext.

Referenced by ast_ari_recordings_copy_stored(), ast_ari_recordings_get_stored(), and ast_ari_recordings_list_stored().

450 {
451  /* Path was validated when the recording object was created */
452  return unlink(recording->file_with_ext);
453 }
454 
456  struct stasis_app_stored_recording *recording)
457 {
458  if (!recording) {
459  return NULL;
460  }
const ast_string_field file_with_ext
Definition: stored.c:41
struct ast_json * stasis_app_stored_recording_to_json(struct stasis_app_stored_recording *recording)
Convert stored recording info to JSON.
Definition: stored.c:450
Abstract JSON element (object, array, string, int, ...).