Asterisk - The Open Source Telephony Project GIT-master-f36a736
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>
Include dependency graph for stored.c:

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. More...
 
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

◆ find_recording()

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 188 of file stored.c.

189{
190 struct match_recording_data data = {
191 .file = file,
192 .length = strlen(file),
193 .file_with_ext = NULL
194 };
195
197
198 /* Note, string potentially allocated in handle_file_recording */
199 return data.file_with_ext;
200}
#define ast_file_read_dir(dir_name, on_file, obj)
Iterate over each file in a given directory.
Definition: file.h:203
#define NULL
Definition: resample.c:96
static int handle_find_recording(const char *dir_name, const char *filename, void *obj)
Definition: stored.c:158
char * file_with_ext
Definition: stored.c:134
const char * file
Definition: stored.c:132

References ast_file_read_dir, match_recording_data::file, make_ari_stubs::file, match_recording_data::file_with_ext, handle_find_recording(), and NULL.

Referenced by stasis_app_stored_recording_find_by_name().

◆ handle_find_recording()

static int handle_find_recording ( const char *  dir_name,
const char *  filename,
void *  obj 
)
static

Definition at line 158 of file stored.c.

159{
160 struct match_recording_data *data = obj;
161 int num;
162
163 /* If not a recording or the names do not match the keep searching */
164 if (!(num = is_recording(filename))
165 || data->length != num
166 || strncmp(data->file, filename, num)) {
167 return 0;
168 }
169
170 if (ast_asprintf(&data->file_with_ext, "%s/%s", dir_name, filename) < 0) {
171 return -1;
172 }
173
174 return 1;
175}
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
static int is_recording(const char *filename)
Definition: stored.c:137

References ast_asprintf, match_recording_data::file, match_recording_data::file_with_ext, is_recording(), and match_recording_data::length.

Referenced by find_recording().

◆ handle_scan_file()

static int handle_scan_file ( const char *  dir_name,
const char *  filename,
void *  obj 
)
static

Definition at line 255 of file stored.c.

256{
257 struct ao2_container *recordings = obj;
258 struct stasis_app_stored_recording *recording;
259 char *dot, *filepath;
260
261 /* Skip if it is not a recording */
262 if (!is_recording(filename)) {
263 return 0;
264 }
265
266 if (ast_asprintf(&filepath, "%s/%s", dir_name, filename) < 0) {
267 return -1;
268 }
269
270 recording = recording_alloc();
271 if (!recording) {
272 ast_free(filepath);
273 return -1;
274 }
275
276 ast_string_field_set(recording, file_with_ext, filepath);
277 /* Build file and format from full path */
278 ast_string_field_set(recording, file, filepath);
279
280 ast_free(filepath);
281
282 dot = strrchr(recording->file, '.');
283 *dot = '\0';
284 recording->format = dot + 1;
285
286 /* Removed the recording dir from the file for the name. */
287 ast_string_field_set(recording, name,
288 recording->file + strlen(ast_config_AST_RECORDING_DIR) + 1);
289
290 /* Add it to the recordings container */
291 ao2_link(recordings, recording);
292 ao2_ref(recording, -1);
293
294 return 0;
295}
#define ast_free(a)
Definition: astmm.h:180
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
static const char name[]
Definition: format_mp3.c:68
const char * ast_config_AST_RECORDING_DIR
Definition: options.c:156
static struct stasis_rest_handlers recordings
REST handler for /api-docs/recordings.json.
static struct stasis_app_stored_recording * recording_alloc(void)
Allocate a recording object.
Definition: stored.c:205
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
Generic container type.
const char * format
Definition: stored.c:43
const ast_string_field file_with_ext
Definition: stored.c:41
const ast_string_field file
Definition: stored.c:41

References ao2_link, ao2_ref, ast_asprintf, ast_config_AST_RECORDING_DIR, ast_free, ast_string_field_set, stasis_app_stored_recording::file, make_ari_stubs::file, stasis_app_stored_recording::file_with_ext, stasis_app_stored_recording::format, is_recording(), name, recording_alloc(), and recordings.

Referenced by stasis_app_stored_recording_find_all().

◆ is_recording()

static int is_recording ( const char *  filename)
static

Definition at line 137 of file stored.c.

138{
139 const char *ext = strrchr(filename, '.');
140
141 if (!ext) {
142 /* No file extension; not us */
143 return 0;
144 }
145 ++ext;
146
148 ast_debug(5, "Recording %s: unrecognized format %s\n",
149 filename, ext);
150 /* Keep looking */
151 return 0;
152 }
153
154 /* Return the index to the .ext */
155 return ext - filename - 1;
156}
struct ast_format * ast_get_format_for_file_ext(const char *file_ext)
Get the ast_format associated with the given file extension.
Definition: file.c:2006
const char * ext
Definition: http.c:150
#define ast_debug(level,...)
Log a DEBUG message.

References ast_debug, ast_get_format_for_file_ext(), and ext.

Referenced by handle_find_recording(), and handle_scan_file().

◆ recording_alloc()

static struct stasis_app_stored_recording * recording_alloc ( void  )
static

Allocate a recording object.

Definition at line 205 of file stored.c.

206{
207 RAII_VAR(struct stasis_app_stored_recording *, recording, NULL,
209 int res;
210
211 recording = ao2_alloc(sizeof(*recording), stored_recording_dtor);
212 if (!recording) {
213 return NULL;
214 }
215
216 res = ast_string_field_init(recording, 255);
217 if (res != 0) {
218 return NULL;
219 }
220
221 ao2_ref(recording, +1);
222 return recording;
223}
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
static void stored_recording_dtor(void *obj)
Definition: stored.c:46
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#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

References ao2_alloc, ao2_cleanup, ao2_ref, ast_string_field_init, NULL, RAII_VAR, and stored_recording_dtor().

Referenced by handle_scan_file(), and stasis_app_stored_recording_find_by_name().

◆ recording_sort()

static int recording_sort ( const void *  obj_left,
const void *  obj_right,
int  flags 
)
static

Definition at line 225 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 }
252 return cmp;
253}
#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
#define ast_assert(a)
Definition: utils.h:739

References ast_assert, stasis_app_stored_recording::name, OBJ_KEY, OBJ_PARTIAL_KEY, and OBJ_POINTER.

Referenced by stasis_app_stored_recording_find_all().

◆ split_path()

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.

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';
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;
129}
void ast_std_free(void *ptr)
Definition: astmm.c:1734
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241

References ast_asprintf, ast_config_AST_RECORDING_DIR, ast_free, ast_std_free(), ast_strdup, make_ari_stubs::file, NULL, and RAII_VAR.

Referenced by stasis_app_stored_recording_find_by_name().

◆ stasis_app_stored_recording_copy()

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 398 of file stored.c.

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 }
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) {
442 *dst_recording = stasis_app_stored_recording_find_by_name(dst_file);
443 }
444
445 return res;
446}
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
int ast_filecopy(const char *oldname, const char *newname, const char *fmt)
Copies a file.
Definition: file.c:1151
#define ast_verb(level,...)
struct stasis_app_stored_recording * stasis_app_stored_recording_find_by_name(const char *name)
Creates a stored recording object, with the given name.
Definition: stored.c:318
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

References ast_asprintf, ast_config_AST_RECORDING_DIR, ast_filecopy(), ast_free, ast_safe_mkdir(), ast_strdupa, ast_verb, stasis_app_stored_recording::file, stasis_app_stored_recording::format, NULL, RAII_VAR, and stasis_app_stored_recording_find_by_name().

Referenced by ast_ari_recordings_copy_stored().

◆ stasis_app_stored_recording_delete()

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 448 of file stored.c.

450{
451 /* Path was validated when the recording object was created */
452 return unlink(recording->file_with_ext);
453}

References stasis_app_stored_recording::file_with_ext.

Referenced by ast_ari_recordings_delete_stored().

◆ stasis_app_stored_recording_find_all()

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 297 of file stored.c.

298{
300 int res;
301
304 if (!recordings) {
305 return NULL;
306 }
307
310 if (res) {
311 ao2_ref(recordings, -1);
312 return NULL;
313 }
314
315 return recordings;
316}
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition: astobj2.h:367
#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
@ AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE
Replace objects with duplicate keys in container.
Definition: astobj2.h:1211
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 int recording_sort(const void *obj_left, const void *obj_right, int flags)
Definition: stored.c:225
static int handle_scan_file(const char *dir_name, const char *filename, void *obj)
Definition: stored.c:255

References AO2_ALLOC_OPT_LOCK_NOLOCK, AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, ao2_container_alloc_rbtree, ao2_ref, ast_config_AST_RECORDING_DIR, ast_file_read_dirs(), handle_scan_file(), NULL, recording_sort(), and recordings.

Referenced by ast_ari_recordings_list_stored().

◆ stasis_app_stored_recording_find_by_name()

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 318 of file stored.c.

320{
321 RAII_VAR(struct stasis_app_stored_recording *, recording, NULL,
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
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;
392 }
393
394 ao2_ref(recording, +1);
395 return recording;
396}
#define ast_log
Definition: astobj2.c:42
#define LOG_WARNING
int errno
static char * find_recording(const char *dir_name, const char *file)
Finds a recording in the given directory.
Definition: stored.c:188
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

References ao2_cleanup, ao2_ref, ast_begins_with(), ast_config_AST_RECORDING_DIR, ast_free, ast_log, ast_std_free(), ast_string_field_build, ast_string_field_set, errno, make_ari_stubs::file, stasis_app_stored_recording::file_with_ext, find_recording(), LOG_WARNING, name, NULL, RAII_VAR, recording_alloc(), and split_path().

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

◆ stasis_app_stored_recording_get_extension()

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.

73{
74 if (!recording) {
75 return NULL;
76 }
77 return recording->format;
78}

References stasis_app_stored_recording::format, and NULL.

Referenced by ast_ari_recordings_get_stored_file().

◆ stasis_app_stored_recording_get_file()

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.

55{
56 if (!recording) {
57 return NULL;
58 }
59 return recording->file;
60}

References stasis_app_stored_recording::file, and NULL.

Referenced by play_on_channel().

◆ stasis_app_stored_recording_get_filename()

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.

64{
65 if (!recording) {
66 return NULL;
67 }
68 return recording->file_with_ext;
69}

References stasis_app_stored_recording::file_with_ext, and NULL.

Referenced by ast_ari_recordings_get_stored_file().

◆ stasis_app_stored_recording_to_json()

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 455 of file stored.c.

457{
458 if (!recording) {
459 return NULL;
460 }
461
462 return ast_json_pack("{ s: s, s: s }",
463 "name", recording->name,
464 "format", recording->format);
465}
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612

References ast_json_pack(), stasis_app_stored_recording::format, stasis_app_stored_recording::name, and NULL.

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

◆ stored_recording_dtor()

static void stored_recording_dtor ( void *  obj)
static

Definition at line 46 of file stored.c.

47{
48 struct stasis_app_stored_recording *recording = obj;
49
51}
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374

References ast_string_field_free_memory.

Referenced by recording_alloc().