Introduced alternative signal handling actions
This commit is contained in:
parent
b4e5afd317
commit
3519b42c14
@ -1 +1 @@
|
|||||||
#define Cdrskin_timestamP "2010.03.04.180102"
|
#define Cdrskin_timestamP "2010.03.05.090948"
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#include <a ssert.h>
|
#include <a ssert.h>
|
||||||
@ -534,8 +535,22 @@ no_non_default_bd_re:;
|
|||||||
static void *write_disc_worker_func(struct w_list *w)
|
static void *write_disc_worker_func(struct w_list *w)
|
||||||
{
|
{
|
||||||
struct burn_drive *d = w->u.write.drive;
|
struct burn_drive *d = w->u.write.drive;
|
||||||
|
char msg[80];
|
||||||
|
|
||||||
|
#define Libburn_protect_write_threaD 1
|
||||||
|
|
||||||
|
#ifdef Libburn_protect_write_threaD
|
||||||
|
sigset_t sigset, oldset;
|
||||||
|
|
||||||
|
/* Protect write thread from being interrupted by external signals */
|
||||||
|
sigfillset(&sigset);
|
||||||
|
sigdelset(&sigset, SIGSEGV);
|
||||||
|
sigdelset(&sigset, SIGILL);
|
||||||
|
pthread_sigmask(SIG_SETMASK, &sigset, &oldset);
|
||||||
|
#endif /* Libburn_protect_write_threaD */
|
||||||
|
|
||||||
d->thread_pid = getpid();
|
d->thread_pid = getpid();
|
||||||
|
d->thread_tid = pthread_self();
|
||||||
d->thread_pid_valid= 1;
|
d->thread_pid_valid= 1;
|
||||||
burn_disc_write_sync(w->u.write.opts, w->u.write.disc);
|
burn_disc_write_sync(w->u.write.opts, w->u.write.disc);
|
||||||
d->thread_pid_valid= 0;
|
d->thread_pid_valid= 0;
|
||||||
@ -545,7 +560,18 @@ static void *write_disc_worker_func(struct w_list *w)
|
|||||||
*/
|
*/
|
||||||
burn_write_opts_free(w->u.write.opts);
|
burn_write_opts_free(w->u.write.opts);
|
||||||
|
|
||||||
|
sprintf(msg, "Write thread on drive %d ended", d->global_index);
|
||||||
|
libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020178,
|
||||||
|
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
msg, 0, 0);
|
||||||
|
|
||||||
|
#ifdef Libburn_protect_write_threaD
|
||||||
|
/* (just in case it would not end with all signals blocked) */
|
||||||
|
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
|
||||||
|
#endif /* Libburn_protect_write_threaD */
|
||||||
|
|
||||||
remove_worker(pthread_self());
|
remove_worker(pthread_self());
|
||||||
|
d->busy = BURN_DRIVE_IDLE;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -653,6 +679,18 @@ int burn_fifo_start(struct burn_source *source, int flag)
|
|||||||
struct fifo_opts o;
|
struct fifo_opts o;
|
||||||
struct burn_source_fifo *fs = source->data;
|
struct burn_source_fifo *fs = source->data;
|
||||||
|
|
||||||
|
#define Libburn_protect_fifo_threaD 1
|
||||||
|
|
||||||
|
#ifdef Libburn_protect_fifo_threaD
|
||||||
|
sigset_t sigset, oldset;
|
||||||
|
|
||||||
|
/* Protect write thread from being interrupted by external signals */
|
||||||
|
sigfillset(&sigset);
|
||||||
|
sigdelset(&sigset, SIGSEGV);
|
||||||
|
sigdelset(&sigset, SIGILL);
|
||||||
|
pthread_sigmask(SIG_SETMASK, &sigset, &oldset);
|
||||||
|
#endif /* Libburn_protect_fifo_threaD */
|
||||||
|
|
||||||
fs->is_started = -1;
|
fs->is_started = -1;
|
||||||
|
|
||||||
/* create and set up ring buffer */;
|
/* create and set up ring buffer */;
|
||||||
@ -668,6 +706,12 @@ int burn_fifo_start(struct burn_source *source, int flag)
|
|||||||
add_worker(Burnworker_type_fifO, NULL,
|
add_worker(Burnworker_type_fifO, NULL,
|
||||||
(WorkerFunc) fifo_worker_func, &o);
|
(WorkerFunc) fifo_worker_func, &o);
|
||||||
fs->is_started = 1;
|
fs->is_started = 1;
|
||||||
|
|
||||||
|
#ifdef Libburn_protect_fifo_threaD
|
||||||
|
/* (just in case it would not end with all signals blocked) */
|
||||||
|
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
|
||||||
|
#endif /* Libburn_protect_fifo_threaD */
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
135
libburn/drive.c
135
libburn/drive.c
@ -376,6 +376,7 @@ struct burn_drive *burn_drive_register(struct burn_drive *d)
|
|||||||
d->busy = BURN_DRIVE_IDLE;
|
d->busy = BURN_DRIVE_IDLE;
|
||||||
d->thread_pid = 0;
|
d->thread_pid = 0;
|
||||||
d->thread_pid_valid = 0;
|
d->thread_pid_valid = 0;
|
||||||
|
memset(&(d->thread_tid), 0, sizeof(d->thread_tid));
|
||||||
d->toc_entries = 0;
|
d->toc_entries = 0;
|
||||||
d->toc_entry = NULL;
|
d->toc_entry = NULL;
|
||||||
d->disc = NULL;
|
d->disc = NULL;
|
||||||
@ -815,10 +816,22 @@ int burn_disc_erasable(struct burn_drive *d)
|
|||||||
enum burn_drive_status burn_drive_get_status(struct burn_drive *d,
|
enum burn_drive_status burn_drive_get_status(struct burn_drive *d,
|
||||||
struct burn_progress *p)
|
struct burn_progress *p)
|
||||||
{
|
{
|
||||||
|
/* --- Part of asynchronous signal handling --- */
|
||||||
|
/* This frequently used call may be used to react on messages from
|
||||||
|
the libburn built-in signal handler.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ts B00225 :
|
||||||
|
If aborting with action 2:
|
||||||
|
catch control thread after it returned from signal handler.
|
||||||
|
Let it run burn_abort(4440,...)
|
||||||
|
*/
|
||||||
|
burn_init_catch_on_abort(0);
|
||||||
|
|
||||||
/* ts A70928 : inform control thread of signal in sub-threads */
|
/* ts A70928 : inform control thread of signal in sub-threads */
|
||||||
if (burn_global_abort_level > 0)
|
if (burn_builtin_triggered_action < 2 && burn_global_abort_level > 0)
|
||||||
burn_global_abort_level++;
|
burn_global_abort_level++;
|
||||||
if (burn_global_abort_level > 5) {
|
if (burn_builtin_triggered_action < 2 && burn_global_abort_level > 5) {
|
||||||
if (burn_global_signal_handler == NULL)
|
if (burn_global_signal_handler == NULL)
|
||||||
kill(getpid(), burn_global_abort_signum);
|
kill(getpid(), burn_global_abort_signum);
|
||||||
else
|
else
|
||||||
@ -828,6 +841,9 @@ enum burn_drive_status burn_drive_get_status(struct burn_drive *d,
|
|||||||
burn_global_abort_level = -1;
|
burn_global_abort_level = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- End of asynchronous signal handling --- */
|
||||||
|
|
||||||
|
|
||||||
if (p != NULL) {
|
if (p != NULL) {
|
||||||
memcpy(p, &(d->progress), sizeof(struct burn_progress));
|
memcpy(p, &(d->progress), sizeof(struct burn_progress));
|
||||||
/* TODO: add mutex */
|
/* TODO: add mutex */
|
||||||
@ -849,9 +865,13 @@ int burn_drive_set_stream_recording(struct burn_drive *d, int recmode,
|
|||||||
|
|
||||||
void burn_drive_cancel(struct burn_drive *d)
|
void burn_drive_cancel(struct burn_drive *d)
|
||||||
{
|
{
|
||||||
|
/* ts B00225 : these mutexes are unnecessary because "= 1" is atomar.
|
||||||
pthread_mutex_lock(&d->access_lock);
|
pthread_mutex_lock(&d->access_lock);
|
||||||
|
*/
|
||||||
d->cancel = 1;
|
d->cancel = 1;
|
||||||
|
/*
|
||||||
pthread_mutex_unlock(&d->access_lock);
|
pthread_mutex_unlock(&d->access_lock);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ts A61007 : defunct because unused */
|
/* ts A61007 : defunct because unused */
|
||||||
@ -1868,17 +1888,12 @@ int burn_abort_pacifier(void *handle, int patience, int elapsed)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Abort any running drive operation and finish libburn.
|
/* ts B00226 : Outsourced backend of burn_abort()
|
||||||
@param patience Maximum number of seconds to wait for drives to finish
|
@param flag bit0= do not call burn_finish()
|
||||||
@param pacifier_func Function to produce appeasing messages. See
|
|
||||||
burn_abort_pacifier() for an example.
|
|
||||||
@return 1 ok, all went well
|
|
||||||
0 had to leave a drive in unclean state
|
|
||||||
<0 severe error, do no use libburn again
|
|
||||||
*/
|
*/
|
||||||
int burn_abort(int patience,
|
int burn_abort_5(int patience,
|
||||||
int (*pacifier_func)(void *handle, int patience, int elapsed),
|
int (*pacifier_func)(void *handle, int patience, int elapsed),
|
||||||
void *handle)
|
void *handle, int elapsed, int flag)
|
||||||
{
|
{
|
||||||
int ret, i, occup, still_not_done= 1, pacifier_off= 0, first_round= 1;
|
int ret, i, occup, still_not_done= 1, pacifier_off= 0, first_round= 1;
|
||||||
unsigned long wait_grain= 100000;
|
unsigned long wait_grain= 100000;
|
||||||
@ -1888,7 +1903,11 @@ int burn_abort(int patience,
|
|||||||
time_t stdio_patience = 3;
|
time_t stdio_patience = 3;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
fprintf(stderr, "libburn_EXPERIMENTAL: burn_abort_5(%d,%d)\n", patience, flag);
|
||||||
|
|
||||||
current_time = start_time = pacifier_time = time(0);
|
current_time = start_time = pacifier_time = time(0);
|
||||||
|
start_time -= elapsed;
|
||||||
end_time = start_time + patience;
|
end_time = start_time + patience;
|
||||||
|
|
||||||
/* >>> ts A71002 : are there any threads at work ?
|
/* >>> ts A71002 : are there any threads at work ?
|
||||||
@ -1935,6 +1954,9 @@ int burn_abort(int patience,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(occup <= 10) {
|
if(occup <= 10) {
|
||||||
|
if (drive_array[i].drive_role != 1)
|
||||||
|
/* occup == -1 comes early */
|
||||||
|
usleep(1000000);
|
||||||
burn_drive_forget(&(drive_array[i]), 1);
|
burn_drive_forget(&(drive_array[i]), 1);
|
||||||
} else if(occup <= 100) {
|
} else if(occup <= 100) {
|
||||||
if(first_round)
|
if(first_round)
|
||||||
@ -1959,12 +1981,95 @@ int burn_abort(int patience,
|
|||||||
pacifier_time = current_time;
|
pacifier_time = current_time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (patience > 0)
|
if (!(flag & 1))
|
||||||
burn_finish();
|
burn_finish();
|
||||||
return(still_not_done == 0);
|
return(still_not_done == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef NIX
|
||||||
|
|
||||||
|
/* <<< did not help. Is on its way out */
|
||||||
|
/* ts B00226 */
|
||||||
|
/* Wait for the most delicate drive states to end
|
||||||
|
*/
|
||||||
|
int burn_abort_write(int patience,
|
||||||
|
int (*pacifier_func)(void *handle, int patience, int elapsed),
|
||||||
|
void *handle)
|
||||||
|
{
|
||||||
|
int ret, i, still_not_done= 1, pacifier_off= 0;
|
||||||
|
unsigned long wait_grain= 100000;
|
||||||
|
time_t start_time, current_time, pacifier_time, end_time;
|
||||||
|
struct burn_drive *d;
|
||||||
|
|
||||||
|
fprintf(stderr, "libburn_EXPERIMENTAL: burn_abort_write\n");
|
||||||
|
|
||||||
|
current_time = start_time = pacifier_time = time(0);
|
||||||
|
end_time = start_time + patience;
|
||||||
|
|
||||||
|
while(current_time < end_time || patience <= 0) {
|
||||||
|
still_not_done = 0;
|
||||||
|
|
||||||
|
for(i = 0; i < drivetop + 1; i++) {
|
||||||
|
d = &(drive_array[i]);
|
||||||
|
if(d->global_index < 0)
|
||||||
|
continue;
|
||||||
|
if(d->busy != BURN_DRIVE_WRITING &&
|
||||||
|
d->busy != BURN_DRIVE_WRITING_LEADIN &&
|
||||||
|
d->busy != BURN_DRIVE_WRITING_LEADOUT &&
|
||||||
|
d->busy != BURN_DRIVE_WRITING_PREGAP &&
|
||||||
|
d->busy != BURN_DRIVE_CLOSING_TRACK &&
|
||||||
|
d->busy != BURN_DRIVE_CLOSING_SESSION)
|
||||||
|
continue;
|
||||||
|
still_not_done = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(still_not_done == 0 || patience <= 0)
|
||||||
|
break;
|
||||||
|
usleep(wait_grain);
|
||||||
|
current_time = time(0);
|
||||||
|
if(current_time>pacifier_time) {
|
||||||
|
if(pacifier_func != NULL && !pacifier_off) {
|
||||||
|
ret = (*pacifier_func)(handle, patience,
|
||||||
|
current_time - start_time);
|
||||||
|
pacifier_off = (ret <= 0);
|
||||||
|
}
|
||||||
|
pacifier_time = current_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current_time - start_time > patience - 3)
|
||||||
|
patience = current_time - start_time + 3;
|
||||||
|
ret = burn_abort_5(patience, pacifier_func, handle,
|
||||||
|
current_time - start_time, 0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* NIX */
|
||||||
|
|
||||||
|
|
||||||
|
/** Abort any running drive operation and finish libburn.
|
||||||
|
@param patience Maximum number of seconds to wait for drives to finish
|
||||||
|
@param pacifier_func Function to produce appeasing messages. See
|
||||||
|
burn_abort_pacifier() for an example.
|
||||||
|
@return 1 ok, all went well
|
||||||
|
0 had to leave a drive in unclean state
|
||||||
|
<0 severe error, do no use libburn again
|
||||||
|
*/
|
||||||
|
int burn_abort(int patience,
|
||||||
|
int (*pacifier_func)(void *handle, int patience, int elapsed),
|
||||||
|
void *handle)
|
||||||
|
{
|
||||||
|
int ret, flg = 0;
|
||||||
|
|
||||||
|
if (patience < 0) {
|
||||||
|
patience = 0;
|
||||||
|
flg |= 1;
|
||||||
|
}
|
||||||
|
ret = burn_abort_5(patience, pacifier_func, handle, 0, flg);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ts A61020 API function */
|
/* ts A61020 API function */
|
||||||
int burn_drive_get_start_end_lba(struct burn_drive *d,
|
int burn_drive_get_start_end_lba(struct burn_drive *d,
|
||||||
int *start_lba, int *end_lba, int flag)
|
int *start_lba, int *end_lba, int flag)
|
||||||
@ -2698,7 +2803,8 @@ int burn_drive_equals_adr(struct burn_drive *d1, char *adr2_in, int role2)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int burn_drive_find_by_thread_pid(struct burn_drive **d, pid_t pid)
|
int burn_drive_find_by_thread_pid(struct burn_drive **d, pid_t pid,
|
||||||
|
pthread_t tid)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -2710,7 +2816,8 @@ int burn_drive_find_by_thread_pid(struct burn_drive **d, pid_t pid)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if (drive_array[i].thread_pid_valid &&
|
if (drive_array[i].thread_pid_valid &&
|
||||||
drive_array[i].thread_pid == pid) {
|
drive_array[i].thread_pid == pid &&
|
||||||
|
pthread_equal(drive_array[i].thread_tid, tid)) {
|
||||||
*d = &(drive_array[i]);
|
*d = &(drive_array[i]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -127,8 +127,9 @@ int burn_disc_get_write_mode_demands(struct burn_disc *disc,
|
|||||||
int burn_drive__fd_from_special_adr(char *adr);
|
int burn_drive__fd_from_special_adr(char *adr);
|
||||||
|
|
||||||
|
|
||||||
/* ts A70929 : Find the drive which is being worked on by pid */
|
/* ts A70929 : Find the drive which is being worked on by pid , tid */
|
||||||
int burn_drive_find_by_thread_pid(struct burn_drive **d, pid_t pid);
|
int burn_drive_find_by_thread_pid(struct burn_drive **d, pid_t pid,
|
||||||
|
pthread_t tid);
|
||||||
|
|
||||||
|
|
||||||
/* ts A51221 - A80731 : Whitelist inquiry functions */
|
/* ts A51221 - A80731 : Whitelist inquiry functions */
|
||||||
@ -140,5 +141,20 @@ char *burn_drive_whitelist_item(int idx, int flag);
|
|||||||
/* ts A80801 */
|
/* ts A80801 */
|
||||||
int burn_drive_is_listed(char *path, struct burn_drive **found, int flag);
|
int burn_drive_is_listed(char *path, struct burn_drive **found, int flag);
|
||||||
|
|
||||||
|
#ifdef NIX
|
||||||
|
/* <<< did not help. Is on its way out */
|
||||||
|
/* ts B00226 */
|
||||||
|
int burn_abort_write(int patience,
|
||||||
|
int (*pacifier_func)(void *handle, int patience, int elapsed),
|
||||||
|
void *handle);
|
||||||
|
#endif /* NIX */
|
||||||
|
|
||||||
|
/* ts B00226 : Outsourced backend of burn_abort()
|
||||||
|
@param elapsed to be subtracted from start time
|
||||||
|
@param flag bit0= do not shutdown the library
|
||||||
|
*/
|
||||||
|
int burn_abort_5(int patience,
|
||||||
|
int (*pacifier_func)(void *handle, int patience, int elapsed),
|
||||||
|
void *handle, int elapsed, int flag);
|
||||||
|
|
||||||
#endif /* __DRIVE */
|
#endif /* __DRIVE */
|
||||||
|
117
libburn/init.c
117
libburn/init.c
@ -15,6 +15,7 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
/* ts A70928 : init.h is for others, not for init .c
|
/* ts A70928 : init.h is for others, not for init .c
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
@ -80,10 +81,13 @@ static char sg_initialize_msg[1024] = {""};
|
|||||||
/* Parameters for builtin abort handler */
|
/* Parameters for builtin abort handler */
|
||||||
static char abort_message_prefix[81] = {"libburn : "};
|
static char abort_message_prefix[81] = {"libburn : "};
|
||||||
static pid_t abort_control_pid= 0;
|
static pid_t abort_control_pid= 0;
|
||||||
|
static pthread_t abort_control_thread;
|
||||||
volatile int burn_global_abort_level= 0;
|
volatile int burn_global_abort_level= 0;
|
||||||
int burn_global_abort_signum= 0;
|
int burn_global_abort_signum= 0;
|
||||||
void *burn_global_signal_handle = NULL;
|
void *burn_global_signal_handle = NULL;
|
||||||
burn_abort_handler_t burn_global_signal_handler = NULL;
|
burn_abort_handler_t burn_global_signal_handler = NULL;
|
||||||
|
int burn_builtin_signal_action = 0; /* burn_set_signal_handling() */
|
||||||
|
volatile int burn_builtin_triggered_action = 0; /* burn_is_aborting() */
|
||||||
|
|
||||||
|
|
||||||
/* ts A70223 : wether implemented untested profiles are supported */
|
/* ts A70223 : wether implemented untested profiles are supported */
|
||||||
@ -329,23 +333,75 @@ int burn_sev_to_text(int severity_number, char **severity_name, int flag)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ts B00224 */
|
||||||
|
char *burn_util_thread_id(pid_t pid, pthread_t tid, char text[80])
|
||||||
|
{
|
||||||
|
int i, l;
|
||||||
|
|
||||||
|
sprintf(text, "[%d,", getpid());
|
||||||
|
l= strlen(text);
|
||||||
|
for(i= 0; i < sizeof(pthread_t) && 2 * i < 80 - l - 3; i++)
|
||||||
|
sprintf(text + l + 2 * i,
|
||||||
|
"%2.2X", ((unsigned char *) &tid)[i]);
|
||||||
|
|
||||||
|
sprintf(text + l + 2 * i, "]");
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
int burn_builtin_abort_handler(void *handle, int signum, int flag)
|
int burn_builtin_abort_handler(void *handle, int signum, int flag)
|
||||||
{
|
{
|
||||||
|
|
||||||
#define Libburn_new_thread_signal_handleR 1
|
#define Libburn_new_thread_signal_handleR 1
|
||||||
/*
|
/*
|
||||||
#define Libburn_signal_handler_verbouS 1
|
|
||||||
*/
|
*/
|
||||||
|
#define Libburn_signal_handler_verbouS 1
|
||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
struct burn_drive *d;
|
struct burn_drive *d;
|
||||||
|
|
||||||
#ifdef Libburn_signal_handler_verbouS
|
#ifdef Libburn_signal_handler_verbouS
|
||||||
fprintf(stderr,
|
char text[80];
|
||||||
"libburn_ABORT: pid = %d , abort_control_pid = %d , sig= %d\n",
|
|
||||||
getpid(), abort_control_pid, signum);
|
fprintf(stderr, "libburn_ABORT: in = %s\n",
|
||||||
|
burn_util_thread_id(getpid(), pthread_self(), text));
|
||||||
|
fprintf(stderr, "libburn_ABORT: ctrl = %s\n",
|
||||||
|
burn_util_thread_id(abort_control_pid, abort_control_thread,
|
||||||
|
text));
|
||||||
|
if (burn_global_signal_handler == burn_builtin_abort_handler)
|
||||||
|
fprintf(stderr, "libburn_ABORT: signal action = %d\n",
|
||||||
|
burn_builtin_signal_action);
|
||||||
|
|
||||||
|
/* >>> find writing drives and report their tid
|
||||||
|
fprintf(stderr, "libburn_ABORT: wrt = %s\n",
|
||||||
|
burn_util_thread_id(0, burn_write_thread_id, text));
|
||||||
|
fprintf(stderr, "libburn_ABORT: sig= %d\n", signum);
|
||||||
|
*/
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
burn_builtin_triggered_action = burn_builtin_signal_action;
|
||||||
|
burn_global_abort_level = -1;
|
||||||
|
|
||||||
|
if (burn_builtin_signal_action > 1) {
|
||||||
|
Cleanup_set_handlers(NULL, NULL, 2);
|
||||||
|
if (burn_builtin_signal_action == 4)
|
||||||
|
return -2;
|
||||||
|
fprintf(stderr,"%sABORT : Trying to shut down busy drives\n",
|
||||||
|
abort_message_prefix);
|
||||||
|
fprintf(stderr,
|
||||||
|
"%sABORT : Wait the normal burning time before any kill -9\n",
|
||||||
|
abort_message_prefix);
|
||||||
|
burn_abort_5(0, burn_abort_pacifier, abort_message_prefix,
|
||||||
|
0, 1);
|
||||||
|
libdax_msgs_submit(libdax_messenger, -1, 0x00020177,
|
||||||
|
LIBDAX_MSGS_SEV_ABORT, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"Urged drive worker threads to do emergency halt",
|
||||||
|
0, 0);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---- old deprecated stuck-in-abort-handler loop ---- */
|
||||||
|
|
||||||
/* ts A70928:
|
/* ts A70928:
|
||||||
Must be quick. Allowed to coincide with other thread and to share
|
Must be quick. Allowed to coincide with other thread and to share
|
||||||
the increment with that one. It must not decrease, though, and
|
the increment with that one. It must not decrease, though, and
|
||||||
@ -358,7 +414,8 @@ int burn_builtin_abort_handler(void *handle, int signum, int flag)
|
|||||||
|
|
||||||
#ifdef Libburn_new_thread_signal_handleR
|
#ifdef Libburn_new_thread_signal_handleR
|
||||||
|
|
||||||
ret = burn_drive_find_by_thread_pid(&d, getpid());
|
ret = burn_drive_find_by_thread_pid(&d, getpid(),
|
||||||
|
pthread_self());
|
||||||
if (ret > 0 && d->busy == BURN_DRIVE_WRITING) {
|
if (ret > 0 && d->busy == BURN_DRIVE_WRITING) {
|
||||||
/* This is an active writer thread */
|
/* This is an active writer thread */
|
||||||
|
|
||||||
@ -398,13 +455,13 @@ int burn_builtin_abort_handler(void *handle, int signum, int flag)
|
|||||||
}
|
}
|
||||||
burn_global_abort_level = -1;
|
burn_global_abort_level = -1;
|
||||||
Cleanup_set_handlers(NULL, NULL, 2);
|
Cleanup_set_handlers(NULL, NULL, 2);
|
||||||
|
|
||||||
fprintf(stderr,"%sABORT : Trying to shut down drive and library\n",
|
fprintf(stderr,"%sABORT : Trying to shut down drive and library\n",
|
||||||
abort_message_prefix);
|
abort_message_prefix);
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"%sABORT : Wait the normal burning time before any kill -9\n",
|
"%sABORT : Wait the normal burning time before any kill -9\n",
|
||||||
abort_message_prefix);
|
abort_message_prefix);
|
||||||
close(0); /* somehow stdin as input blocks abort until EOF */
|
close(0); /* somehow stdin as input blocks abort until EOF */
|
||||||
|
|
||||||
burn_abort(4440, burn_abort_pacifier, abort_message_prefix);
|
burn_abort(4440, burn_abort_pacifier, abort_message_prefix);
|
||||||
|
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
@ -414,6 +471,8 @@ int burn_builtin_abort_handler(void *handle, int signum, int flag)
|
|||||||
return(1);
|
return(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61002 : API */
|
||||||
void burn_set_signal_handling(void *handle, burn_abort_handler_t handler,
|
void burn_set_signal_handling(void *handle, burn_abort_handler_t handler,
|
||||||
int mode)
|
int mode)
|
||||||
{
|
{
|
||||||
@ -436,12 +495,56 @@ void burn_set_signal_handling(void *handle, burn_abort_handler_t handler,
|
|||||||
sizeof(abort_message_prefix)-1);
|
sizeof(abort_message_prefix)-1);
|
||||||
abort_message_prefix[sizeof(abort_message_prefix)-1] = 0;
|
abort_message_prefix[sizeof(abort_message_prefix)-1] = 0;
|
||||||
abort_control_pid = getpid();
|
abort_control_pid = getpid();
|
||||||
Cleanup_set_handlers(handle, (Cleanup_app_handler_T) handler, mode|4);
|
abort_control_thread = pthread_self();
|
||||||
|
burn_builtin_signal_action = (mode >> 4) & 15;
|
||||||
|
if((mode & 11) != 0)
|
||||||
|
burn_builtin_signal_action = 0;
|
||||||
|
if(burn_builtin_signal_action > 1)
|
||||||
|
burn_builtin_triggered_action = 0;
|
||||||
|
if(burn_builtin_signal_action == 0)
|
||||||
|
burn_builtin_signal_action = 1;
|
||||||
|
|
||||||
|
fprintf(stderr, "libburn_EXPERIMENTAL: mode = %d , burn_builtin_signal_action = %d\n",
|
||||||
|
mode, burn_builtin_signal_action);
|
||||||
|
|
||||||
|
Cleanup_set_handlers(handle, (Cleanup_app_handler_T) handler,
|
||||||
|
(mode & 15) | 4);
|
||||||
burn_global_signal_handle = handle;
|
burn_global_signal_handle = handle;
|
||||||
burn_global_signal_handler = handler;
|
burn_global_signal_handler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts B00304 : API */
|
||||||
|
int burn_is_aborting(int flag)
|
||||||
|
{
|
||||||
|
return burn_builtin_triggered_action;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts B00225 */
|
||||||
|
/* @return 0= no abort action 2 pending , 1= not control thread
|
||||||
|
*/
|
||||||
|
int burn_init_catch_on_abort(int flag)
|
||||||
|
{
|
||||||
|
if (burn_builtin_triggered_action != 2)
|
||||||
|
return 0;
|
||||||
|
if (abort_control_pid != getpid() ||
|
||||||
|
abort_control_thread != pthread_self())
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
#ifdef NIX
|
||||||
|
burn_abort_write(4440, burn_abort_pacifier, abort_message_prefix);
|
||||||
|
#else
|
||||||
|
burn_abort(4440, burn_abort_pacifier, abort_message_prefix);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
fprintf(stderr,
|
||||||
|
"\n%sABORT : Program done. Even if you do not see a shell prompt.\n\n",
|
||||||
|
abort_message_prefix);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ts A70223 : API */
|
/* ts A70223 : API */
|
||||||
void burn_allow_untested_profiles(int yes)
|
void burn_allow_untested_profiles(int yes)
|
||||||
{
|
{
|
||||||
|
@ -17,5 +17,15 @@ extern int burn_global_abort_signum;
|
|||||||
extern void *burn_global_signal_handle;
|
extern void *burn_global_signal_handle;
|
||||||
extern burn_abort_handler_t burn_global_signal_handler;
|
extern burn_abort_handler_t burn_global_signal_handler;
|
||||||
|
|
||||||
|
extern int burn_builtin_signal_action; /* burn_set_signal_handling() */
|
||||||
|
extern volatile int burn_builtin_triggered_action; /* burn_is_aborting() */
|
||||||
|
|
||||||
|
|
||||||
|
/* ts B00225 */
|
||||||
|
/* @return 0= no abort pending , 1= not control thread ,
|
||||||
|
-1= surprisingly burn_abort returned
|
||||||
|
*/
|
||||||
|
int burn_init_catch_on_abort(int flag);
|
||||||
|
|
||||||
|
|
||||||
#endif /* BURN__INIT_H */
|
#endif /* BURN__INIT_H */
|
||||||
|
@ -736,14 +736,19 @@ void burn_finish(void);
|
|||||||
|
|
||||||
|
|
||||||
/* ts A61002 */
|
/* ts A61002 */
|
||||||
/** Abort any running drive operation and finally call burn_finish().
|
/** Abort any running drive operation and eventually call burn_finish().
|
||||||
You MUST calm down the busy drive if an aborting event occurs during a
|
|
||||||
|
You MUST shut down the busy drives if an aborting event occurs during a
|
||||||
burn run. For that you may call this function either from your own signal
|
burn run. For that you may call this function either from your own signal
|
||||||
handling code or indirectly by activating the builtin signal handling:
|
handling code or indirectly by activating the built-in signal handling:
|
||||||
burn_set_signal_handling("my_app_name : ", NULL, 0);
|
burn_set_signal_handling("my_app_name : ", NULL, 0);
|
||||||
Else you may eventually call burn_drive_cancel() on the active drive and
|
Else you may eventually call burn_drive_cancel() on the active drives and
|
||||||
wait for it to assume state BURN_DRIVE_IDLE.
|
wait for them to assume state BURN_DRIVE_IDLE.
|
||||||
@param patience Maximum number of seconds to wait for drives to finish
|
@param patience Maximum number of seconds to wait for drives to
|
||||||
|
finish.
|
||||||
|
@since 0.7.8 :
|
||||||
|
If this is -1, then only the cancel operations will
|
||||||
|
be performed and no burn_finish() will happen.
|
||||||
@param pacifier_func If not NULL: a function to produce appeasing messages.
|
@param pacifier_func If not NULL: a function to produce appeasing messages.
|
||||||
See burn_abort_pacifier() for an example.
|
See burn_abort_pacifier() for an example.
|
||||||
@param handle Opaque handle to be used with pacifier_func
|
@param handle Opaque handle to be used with pacifier_func
|
||||||
@ -2784,30 +2789,78 @@ int burn_set_messenger(void *messenger);
|
|||||||
|
|
||||||
/* ts A61002 */
|
/* ts A61002 */
|
||||||
/* @since 0.2.6 */
|
/* @since 0.2.6 */
|
||||||
/** The prototype of a handler function suitable for burn_set_abort_handling().
|
/** The prototype of a handler function suitable for burn_set_signal_handling()
|
||||||
Such a function has to return -2 if it does not want the process to
|
Such a function has to return -2 if it does not want the process to
|
||||||
exit with value 1.
|
exit with value 1.
|
||||||
*/
|
*/
|
||||||
typedef int (*burn_abort_handler_t)(void *handle, int signum, int flag);
|
typedef int (*burn_abort_handler_t)(void *handle, int signum, int flag);
|
||||||
|
|
||||||
/** Control builtin signal handling. See also burn_abort().
|
/** Control built-in signal handling. Either by setting an own handler or
|
||||||
|
by activating the built-in signal handler.
|
||||||
|
|
||||||
|
A function parameter handle of NULL activates the built-in abort handler.
|
||||||
|
Depending on mode it may cancel all drive operations, wait for all drives
|
||||||
|
to become idle, exit(1). It may also prepare function
|
||||||
|
burn_drive_get_status() for waiting and performing exit(1).
|
||||||
|
If text is not NULL then it is used as prefix for pacifier messages of
|
||||||
|
burn_abort_pacifier().
|
||||||
|
Before version 0.7.8 only action 0 was available. I.e. the built-in handler
|
||||||
|
waited for the drives to become idle and then performed exit(1) directly.
|
||||||
|
But FreeBSD 8.0 sometimes pauses the other threads until the signal handler
|
||||||
|
returns.
|
||||||
|
The new actions try to avoid this deadlock. It is advised to use action 3
|
||||||
|
burn_set_signal_handling(text, NULL, 48);
|
||||||
|
and call burn_is_aborting(0) frequently. If it replies 1, then call
|
||||||
|
burn_abort() and exit(1).
|
||||||
@param handle Opaque handle eventually pointing to an application
|
@param handle Opaque handle eventually pointing to an application
|
||||||
provided memory object
|
provided memory object
|
||||||
@param handler A function to be called on signals. It will get handle as
|
@param handler A function to be called on signals. It will get handle as
|
||||||
argument. It should finally call burn_abort(). See there.
|
argument. flag will be 0.
|
||||||
@param mode : 0 call handler(handle, signum, 0) on nearly all signals
|
It should finally call burn_abort(). See there.
|
||||||
1 enable system default reaction on all signals
|
@param mode : bit0 - bit3:
|
||||||
2 try to ignore nearly all signals
|
Receiving signals:
|
||||||
|
0 Call handler(handle, signum, 0) on nearly all signals
|
||||||
|
1 Enable system default reaction on all signals
|
||||||
|
2 Try to ignore nearly all signals
|
||||||
10 like mode 2 but handle SIGABRT like with mode 0
|
10 like mode 2 but handle SIGABRT like with mode 0
|
||||||
Arguments (text, NULL, 0) activate the builtin abort handler. It will
|
bit4 - bit7: With handler == NULL :
|
||||||
eventually call burn_abort() and then perform exit(1). If text is not NULL
|
Action of built-in handler. "control thread" is the one
|
||||||
then it is used as prefix for pacifier messages of burn_abort_pacifier().
|
which called burn_set_signal_handling().
|
||||||
|
All actions activate receive mode 2 to ignore further
|
||||||
|
signals.
|
||||||
|
0 Same as 1 (for pre-0.7.8 backward compatibility)
|
||||||
|
@since 0.7.8
|
||||||
|
1 Catch the control thread in abort handler, call
|
||||||
|
burn_abort(>0) and finally exit(1).
|
||||||
|
Does not always work with FreeBSD.
|
||||||
|
2 Call burn_abort(-1) and return from handler. When the
|
||||||
|
control thread calls burn_drive_get_status(), then do
|
||||||
|
burn_abort(>0) instead, and finally exit(1).
|
||||||
|
Does not always work with FreeBSD.
|
||||||
|
3 Call burn_abort(-1), return from handler. It is duty of
|
||||||
|
the application to detect a pending abort condition
|
||||||
|
by calling burn_is_aborting() and to wait for all
|
||||||
|
drives to become idle. E.g. by calling burn_abort(>0).
|
||||||
|
4 Like 3, but without calling burn_abort(-1). Only the
|
||||||
|
indicator of burn_is_aborting() gets set.
|
||||||
@since 0.2.6
|
@since 0.2.6
|
||||||
*/
|
*/
|
||||||
void burn_set_signal_handling(void *handle, burn_abort_handler_t handler,
|
void burn_set_signal_handling(void *handle, burn_abort_handler_t handler,
|
||||||
int mode);
|
int mode);
|
||||||
|
|
||||||
|
|
||||||
|
/* ts B00304 */
|
||||||
|
/* Inquire whether the built-in abort handler was triggered by a signal.
|
||||||
|
This has to be done to detect pending abort handling if signal handling
|
||||||
|
was set to the built-in handler and action was set to 2 or 3.
|
||||||
|
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||||
|
@return 0 = no abort was triggered
|
||||||
|
>0 = action that was triggered (action 0 is reported as 1)
|
||||||
|
@since 0.7.8
|
||||||
|
*/
|
||||||
|
int burn_is_aborting(int flag);
|
||||||
|
|
||||||
|
|
||||||
/* ts A70811 */
|
/* ts A70811 */
|
||||||
/** Write data in random access mode.
|
/** Write data in random access mode.
|
||||||
The drive must be grabbed successfully before calling this function which
|
The drive must be grabbed successfully before calling this function which
|
||||||
@ -2955,5 +3008,11 @@ BURN_END_DECLS
|
|||||||
#define Libburn_dummy_probe_write_modeS 1
|
#define Libburn_dummy_probe_write_modeS 1
|
||||||
|
|
||||||
|
|
||||||
|
/* ts B00225 */
|
||||||
|
/* Leave abort handler quickly and catch control thread in
|
||||||
|
burn_drive_get_status().
|
||||||
|
*/
|
||||||
|
#define Libburn_signal_handler_return_2 1
|
||||||
|
|
||||||
|
|
||||||
#endif /*LIBBURN_H*/
|
#endif /*LIBBURN_H*/
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
/* Only this single source module is entitled to do this */
|
/* Only this single source module is entitled to do this */
|
||||||
#define LIBDAX_MSGS_H_INTERNAL 1
|
#define LIBDAX_MSGS_H_INTERNAL 1
|
||||||
|
@ -554,6 +554,8 @@ Range "scdbackup" : 0x00020000 to 0x0002ffff
|
|||||||
0x00020174 (SORRY,HIGH) = Fifo alignment does not allow desired read size
|
0x00020174 (SORRY,HIGH) = Fifo alignment does not allow desired read size
|
||||||
0x00020175 (FATAL,HIGH) = Supporting library is too old
|
0x00020175 (FATAL,HIGH) = Supporting library is too old
|
||||||
0x00020176 (NOTE,HIGH) = Stream recording disabled because of small OS buffer
|
0x00020176 (NOTE,HIGH) = Stream recording disabled because of small OS buffer
|
||||||
|
0x00020177 (ABORT,HIGH) = Urged drive worker threads to do emergency halt
|
||||||
|
0x00020178 (DEBUG,HIGH) = Write thread ended
|
||||||
|
|
||||||
|
|
||||||
libdax_audioxtr:
|
libdax_audioxtr:
|
||||||
|
@ -31,7 +31,7 @@ extern struct libdax_msgs *libdax_messenger;
|
|||||||
|
|
||||||
|
|
||||||
#ifdef Libburn_log_in_and_out_streaM
|
#ifdef Libburn_log_in_and_out_streaM
|
||||||
/* <<< ts A61031 */
|
/* ts A61031 */
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@ -102,7 +102,7 @@ static void get_bytes(struct burn_track *track, int count, unsigned char *data)
|
|||||||
int valid, shortage, curr, i, tr;
|
int valid, shortage, curr, i, tr;
|
||||||
|
|
||||||
#ifdef Libburn_log_in_and_out_streaM
|
#ifdef Libburn_log_in_and_out_streaM
|
||||||
/* <<< ts A61031 */
|
/* ts A61031 */
|
||||||
static int tee_fd= -1;
|
static int tee_fd= -1;
|
||||||
if(tee_fd==-1)
|
if(tee_fd==-1)
|
||||||
tee_fd= open("/tmp/libburn_sg_readin",
|
tee_fd= open("/tmp/libburn_sg_readin",
|
||||||
@ -148,7 +148,7 @@ static void get_bytes(struct burn_track *track, int count, unsigned char *data)
|
|||||||
track->sourcecount += valid;
|
track->sourcecount += valid;
|
||||||
|
|
||||||
#ifdef Libburn_log_in_and_out_streaM
|
#ifdef Libburn_log_in_and_out_streaM
|
||||||
/* <<< ts A61031 */
|
/* ts A61031 */
|
||||||
if(tee_fd!=-1 && valid>0) {
|
if(tee_fd!=-1 && valid>0) {
|
||||||
write(tee_fd, data + curr, valid);
|
write(tee_fd, data + curr, valid);
|
||||||
}
|
}
|
||||||
@ -678,7 +678,7 @@ int sector_data(struct burn_write_opts *o, struct burn_track *t, int psub)
|
|||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
|
|
||||||
data = get_sector(o, t, t->mode);
|
data = get_sector(o, t, t->mode);
|
||||||
if (!data)
|
if (data == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
/* ts A61010 */
|
/* ts A61010 */
|
||||||
if (convert_data(o, t, t->mode, data) <= 0)
|
if (convert_data(o, t, t->mode, data) <= 0)
|
||||||
|
@ -312,6 +312,8 @@ struct burn_drive
|
|||||||
/* ts A70929 */
|
/* ts A70929 */
|
||||||
pid_t thread_pid;
|
pid_t thread_pid;
|
||||||
int thread_pid_valid;
|
int thread_pid_valid;
|
||||||
|
/* ts B00225 */
|
||||||
|
pthread_t thread_tid;
|
||||||
|
|
||||||
|
|
||||||
/* transport functions */
|
/* transport functions */
|
||||||
|
@ -1312,7 +1312,7 @@ static int transact_dvd_chunk(struct burn_write_opts *opts,
|
|||||||
unsigned char *data = out->data;
|
unsigned char *data = out->data;
|
||||||
|
|
||||||
#ifdef Libburn_log_in_and_out_streaM
|
#ifdef Libburn_log_in_and_out_streaM
|
||||||
/* <<< ts A61031 */
|
/* ts A61031 */
|
||||||
static int tee_fd= -1;
|
static int tee_fd= -1;
|
||||||
if(tee_fd==-1)
|
if(tee_fd==-1)
|
||||||
tee_fd= open("/tmp/libburn_sg_readin",
|
tee_fd= open("/tmp/libburn_sg_readin",
|
||||||
@ -2002,7 +2002,7 @@ ex:;
|
|||||||
burn_drive_mark_unready(d);
|
burn_drive_mark_unready(d);
|
||||||
burn_drive_inquire_media(d);
|
burn_drive_inquire_media(d);
|
||||||
|
|
||||||
d->busy = BURN_DRIVE_IDLE;
|
/* <<< d->busy = BURN_DRIVE_IDLE; */
|
||||||
return ret;
|
return ret;
|
||||||
early_failure:;
|
early_failure:;
|
||||||
return 0;
|
return 0;
|
||||||
@ -2330,7 +2330,7 @@ ex:;
|
|||||||
/* update media state records */
|
/* update media state records */
|
||||||
burn_drive_mark_unready(d);
|
burn_drive_mark_unready(d);
|
||||||
|
|
||||||
d->busy = BURN_DRIVE_IDLE;
|
/* <<< d->busy = BURN_DRIVE_IDLE; */
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2567,7 +2567,7 @@ return crap. so we send the command, then ignore the result.
|
|||||||
burn_drive_inquire_media(d);
|
burn_drive_inquire_media(d);
|
||||||
|
|
||||||
burn_print(1, "done\n");
|
burn_print(1, "done\n");
|
||||||
d->busy = BURN_DRIVE_IDLE;
|
/* <<< d->busy = BURN_DRIVE_IDLE; */
|
||||||
|
|
||||||
/* ts A61012 : This return was traditionally missing. I suspect this
|
/* ts A61012 : This return was traditionally missing. I suspect this
|
||||||
to have caused Cdrskin_eject() failures */
|
to have caused Cdrskin_eject() failures */
|
||||||
@ -2582,7 +2582,7 @@ fail_wo_sync:;
|
|||||||
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
"Burn run failed", 0, 0);
|
"Burn run failed", 0, 0);
|
||||||
d->cancel = 1;
|
d->cancel = 1;
|
||||||
d->busy = BURN_DRIVE_IDLE;
|
/* <<< d->busy = BURN_DRIVE_IDLE; */
|
||||||
ex:;
|
ex:;
|
||||||
d->do_stream_recording = 0;
|
d->do_stream_recording = 0;
|
||||||
if (d->buffer != NULL)
|
if (d->buffer != NULL)
|
||||||
|
Loading…
Reference in New Issue
Block a user