- Test multiple simultaneous Volumes
- Document FInclude ...
+- Test and implement get_pint and get_yesno.
+- Implement timeout in response() when it should come quickly.
+
- Figure out how to use ssh or stunnel to protect Bacula communications.
After 1.30:
# $1 = path to executable
# $2 = main pid of running program to be traced back.
#
-
-gdb -quiet -batch -x @sbindir@/btraceback.gdb $1 $2 2>&1 | mail -s "Bacula traceback" @dump_email@
+gdb -quiet -batch -x @sbindir@/btraceback.gdb $1 $2 2>&1 \
+ | @sbindir@/smtp -h @smtp_host@ -s "Bacula traceback" @dump_email@
# Below is some old code that did the traceback from a core
# dump. However, for some odd reason, core dumps are not
<widget>
<class>GtkLabel</class>
<name>label107</name>
- <width>70</width>
+ <width>78</width>
<label>Storage:</label>
<justify>GTK_JUSTIFY_CENTER</justify>
<wrap>False</wrap>
<widget>
<class>GtkLabel</class>
<name>label109</name>
- <width>70</width>
+ <width>78</width>
<label>Pool:</label>
<justify>GTK_JUSTIFY_LEFT</justify>
<wrap>False</wrap>
<widget>
<class>GtkLabel</class>
<name>label111</name>
- <width>70</width>
+ <width>78</width>
<label>Volume Name:</label>
<justify>GTK_JUSTIFY_LEFT</justify>
<wrap>False</wrap>
- <xalign>0.18</xalign>
+ <xalign>0.17</xalign>
<yalign>0.5</yalign>
<xpad>0</xpad>
<ypad>0</ypad>
</widget>
</widget>
+ <widget>
+ <class>GtkHBox</class>
+ <name>hbox48</name>
+ <border_width>5</border_width>
+ <homogeneous>False</homogeneous>
+ <spacing>0</spacing>
+ <child>
+ <padding>0</padding>
+ <expand>True</expand>
+ <fill>True</fill>
+ </child>
+
+ <widget>
+ <class>GtkLabel</class>
+ <name>slot1</name>
+ <width>93</width>
+ <label>Slot:</label>
+ <justify>GTK_JUSTIFY_LEFT</justify>
+ <wrap>False</wrap>
+ <xalign>0.09</xalign>
+ <yalign>0.5</yalign>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ <child>
+ <padding>0</padding>
+ <expand>False</expand>
+ <fill>True</fill>
+ </child>
+ </widget>
+
+ <widget>
+ <class>GtkSpinButton</class>
+ <name>label_slot</name>
+ <can_focus>True</can_focus>
+ <climb_rate>1</climb_rate>
+ <digits>0</digits>
+ <numeric>True</numeric>
+ <update_policy>GTK_UPDATE_ALWAYS</update_policy>
+ <snap>False</snap>
+ <wrap>False</wrap>
+ <value>0</value>
+ <lower>0</lower>
+ <upper>10000</upper>
+ <step>1</step>
+ <page>10</page>
+ <page_size>10</page_size>
+ <child>
+ <padding>0</padding>
+ <expand>True</expand>
+ <fill>True</fill>
+ </child>
+ </widget>
+
+ <widget>
+ <class>Placeholder</class>
+ </widget>
+ </widget>
+
<widget>
<class>GtkLabel</class>
<name>label113</name>
V(mutex);
/* Try again */
}
+ jcr->acquired_resource_locks = 1;
#endif
return 1;
}
*/
static void release_resource_locks(JCR *jcr)
{
+ if (!jcr->acquired_resource_locks) {
+ return; /* Job canceled, no locks acquired */
+ }
#ifdef USE_SEMAPHORE
P(mutex);
sem_unlock(&jcr->store->sem);
/* ua_input.c */
int get_cmd(UAContext *ua, char *prompt);
+int get_pint(UAContext *ua, char *prompt);
+int get_yesno(UAContext *ua, char *prompt);
void parse_ua_args(UAContext *ua);
/* ua_output.c */
int find_arg_keyword(UAContext *ua, char **list);
int find_arg(UAContext *ua, char *keyword);
+int find_arg_with_value(UAContext *ua, char *keyword);
int do_keyword_prompt(UAContext *ua, char *msg, char **list);
int confirm_retention(UAContext *ua, utime_t *ret, char *msg);
int automount; /* if set, mount after label */
int quit; /* if set, quit */
int verbose; /* set for normal UA verbosity */
+ uint32_t pint32_val; /* positive integer */
+ int32_t int32_val; /* positive/negative */
} UAContext;
#endif
}
getVolName:
if (num == 0) {
- if (!get_cmd(ua, _("Enter Volume name: ")) || ua->cmd[0] == '.') {
+ if (!get_cmd(ua, _("Enter Volume name: "))) {
return 1;
}
} else {
- if (!get_cmd(ua, _("Enter base volume name: ")) || ua->cmd[0] == '.') {
+ if (!get_cmd(ua, _("Enter base volume name: "))) {
return 1;
}
}
}
if (store && store->autochanger) {
- if (!get_cmd(ua, _("Enter slot (0 for none): ")) || ua->cmd[0] == '.') {
+ if (!get_cmd(ua, _("Enter slot (0 for none): "))) {
return 1;
}
slot = atoi(ua->cmd);
return 1;
}
if (njobs == 1) {
- if (!get_cmd(ua, _("Confirm cancel (yes/no): "))) {
- return 1;
- }
- if (strcasecmp(ua->cmd, _("yes")) != 0) {
+ if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
return 1;
}
}
Dmsg1(120, "setdebug:%s:\n", cmd);
level = -1;
- for (i=1; i<ua->argc; i++) {
- if (strcasecmp(ua->argk[i], _("level")) == 0 && ua->argv[i]) {
- level = atoi(ua->argv[i]);
- break;
- }
+ i = find_arg_with_value(ua, _("level"));
+ if (i >= 0) {
+ level = atoi(ua->argv[i]);
}
if (level < 0) {
if (!get_cmd(ua, _("Enter new debug level: "))) {
JCR *jcr = NULL;
int a;
+ bsendmsg(ua, "The Director will segment fault.\n");
a = jcr->JobId; /* ref NULL pointer */
+ jcr->JobId = 1000; /* another ref NULL pointer */
return 0;
}
if (strcmp(ua->cmd, ".messages") == 0) {
qmessagescmd(ua, ua->cmd);
}
- /* ****FIXME**** if .command, go off and do it. For now ignore it. */
- if (ua->cmd[0] == '.' && ua->cmd[1] != 0) {
- continue; /* dot command */
+ /* Lone dot => break */
+ if (ua->cmd[0] == '.' && ua->cmd[1] == 0) {
+ return 0;
}
- /* Lone dot => break or actual response */
break;
}
return 1;
}
+/*
+ * Get a positive integer
+ * Returns: 0 if failure
+ * 1 if success => value in ua->pint32_val
+ */
+int get_pint(UAContext *ua, char *prompt)
+{
+ double dval;
+ ua->pint32_val = 0;
+ for (;;) {
+ if (!get_cmd(ua, prompt)) {
+ return 0;
+ }
+ if (!is_a_number(ua->cmd)) {
+ bsendmsg(ua, "Expected a positive integer, got: %s\n", ua->cmd);
+ continue;
+ }
+ errno = 0;
+ dval = strtod(ua->cmd, NULL);
+ if (errno != 0 || dval < 0) {
+ bsendmsg(ua, "Expected a positive integer, got: %s\n", ua->cmd);
+ continue;
+ }
+ ua->pint32_val = (uint32_t)dval;
+ return 1;
+ }
+}
+
+/*
+ * Gets a yes or no response
+ * Returns: 0 if failure
+ * 1 if success => ua->pint32_val == 1 for yes
+ * ua->pint32_val == 0 for no
+ */
+int get_yesno(UAContext *ua, char *prompt)
+{
+ int len;
+
+ ua->pint32_val = 0;
+ for (;;) {
+ if (!get_cmd(ua, prompt)) {
+ return 0;
+ }
+ len = strlen(ua->cmd);
+ if (len < 1 || len > 3) {
+ continue;
+ }
+ if (strncasecmp(ua->cmd, _("yes"), len) == 0) {
+ ua->pint32_val = 1;
+ return 1;
+ }
+ if (strncasecmp(ua->cmd, _("no"), len) == 0) {
+ return 1;
+ }
+ bsendmsg(ua, _("Invalid response. You must answer yes or no.\n"));
+ }
+}
+
+
void parse_ua_args(UAContext *ua)
{
return parse_command_args(ua->cmd, ua->args, &ua->argc, ua->argk, ua->argv);
/* If autochanger, request slot */
if (store->autochanger) {
+ int first = 1;
for ( ;; ) {
- if (!get_cmd(ua, _("Enter slot (0 for none): ")) || ua->cmd[0] == '.') {
- return 1;
+ if (first) {
+ i = find_arg(ua, "slot");
+ if (i >= 0 && ua->argv[i]) {
+ mr.Slot = atoi(ua->argv[i]);
+ }
+ first = 0;
+ } else {
+ if (!get_cmd(ua, _("Enter slot (0 for none): ")) || ua->cmd[0] == '.') {
+ return 1;
+ }
+ mr.Slot = atoi(ua->cmd);
}
- mr.Slot = atoi(ua->cmd);
if (mr.Slot >= 0) { /* OK */
break;
}
bsendmsg(ua, _("Slot numbers must be positive.\n"));
}
}
+
bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
/* Must select Pool if not already done */
return -1;
}
+int find_arg_with_value(UAContext *ua, char *keyword)
+{
+ for (int i=1; i<ua->argc; i++) {
+ if (strcasecmp(keyword, ua->argk[i]) == 0) {
+ if (ua->argv[i]) {
+ return i;
+ } else {
+ return -1;
+ }
+ }
+ }
+ return -1;
+}
+
+
/*
* Given a list of keywords, prompt the user
memset(mr, 0, sizeof(MEDIA_DBR));
- i = find_arg(ua, "volume");
- if (i >= 0 && ua->argv[i]) {
+ i = find_arg_with_value(ua, "volume");
+ if (i >= 0) {
bstrncpy(mr->VolumeName, ua->argv[i], sizeof(mr->VolumeName));
}
if (mr->VolumeName[0] == 0) {
POOL *pool = NULL;
int i;
- for (i=1; i<ua->argc; i++) {
- if (strcasecmp(ua->argk[i], _("pool")) == 0 && ua->argv[i]) {
- pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
- if (pool) {
- return pool;
- }
- bsendmsg(ua, _("Error: Pool resource %s does not exist.\n"), ua->argv[i]);
- break;
+ i = find_arg_with_value(ua, "pool");
+ if (i >= 0) {
+ pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
+ if (pool) {
+ return pool;
}
+ bsendmsg(ua, _("Error: Pool resource %s does not exist.\n"), ua->argv[i]);
}
return select_pool_resource(ua);
}
sprintf(pmsg, "%s (1-%d): ", msg, ua->num_prompts-1);
}
/* Either a . or an @ will get you out of the loop */
- if (!get_cmd(ua, pmsg) || *ua->cmd == '.' || *ua->cmd == '@') {
+ if (!get_cmd(ua, pmsg) || *ua->cmd == '@') {
item = -1; /* error */
bsendmsg(ua, _("Selection aborted, nothing done.\n"));
break;
STORE *store;
int i;
- i = find_arg(ua, "mediatype");
- if (i >= 0 && ua->argv[i]) {
+ i = find_arg_with_value(ua, "mediatype");
+ if (i >= 0) {
bstrncpy(MediaType, ua->argv[i], max_media);
return 1;
}
* a heartbeat, we simply send it on to the Director to
* keep him alive.
*/
- for ( ;; ) {
- n = bnet_recv(sd);
- if (is_bnet_stop(sd)) {
- break;
+ for ( ; !is_bnet_stop(sd); ) {
+ n = bnet_wait_data_intr(sd, 60);
+ if (n != 1) {
+ continue;
}
+ n = bnet_recv(sd);
if (n == BNET_SIGNAL && sd->msglen == BNET_HEARTBEAT) {
bnet_sig(dir, BNET_HEARTBEAT);
}
}
bnet_close(sd);
bnet_close(dir);
+ jcr->duped_sd = NULL;
return NULL;
}
bmicrosleep(0, 500); /* avoid race */
}
jcr->duped_sd->timed_out = 1; /* set timed_out to terminate read */
+ jcr->duped_sd->terminated = 1; /* set to terminate read */
- pthread_kill(hbtid, TIMEOUT_SIGNAL); /* make heartbeat thread go away */
+ while (jcr->duped_sd) {
+ pthread_kill(hbtid, TIMEOUT_SIGNAL); /* make heartbeat thread go away */
+ bmicrosleep(0, 20);
+ }
pthread_join(hbtid, NULL); /* wait for him to clean up */
}
char *RestoreWhere; /* Where to restore the files */
POOLMEM *client_uname; /* client uname */
int replace; /* Replace option */
+ int acquired_resource_locks; /* set if resource locks acquired */
#endif /* DIRECTOR_DAEMON */
* -1 if error
*/
int
-bnet_wait_data(BSOCK *bsock, int sec)
+bnet_wait_data(BSOCK *bsock, int sec)
{
fd_set fdset;
struct timeval tv;
}
}
+/*
+ * As above, but returns on interrupt
+ */
+int
+bnet_wait_data_intr(BSOCK *bsock, int sec)
+{
+ fd_set fdset;
+ struct timeval tv;
+
+ FD_ZERO(&fdset);
+ FD_SET(bsock->fd, &fdset);
+ tv.tv_sec = sec;
+ tv.tv_usec = 0;
+ for ( ;; ) {
+ switch(select(bsock->fd + 1, &fdset, NULL, NULL, &tv)) {
+ case 0: /* timeout */
+ bsock->b_errno = 0;
+ return 0;
+ case -1:
+ bsock->b_errno = errno;
+ return -1; /* error return */
+ default:
+ bsock->b_errno = 0;
+ return 1;
+ }
+ }
+}
+
+
static pthread_mutex_t ip_mutex = PTHREAD_MUTEX_INITIALIZER;
/*
BSOCK * bnet_connect (void *jcr, int retry_interval,
int max_retry_time, char *name, char *host, char *service,
int port, int verbose);
-int bnet_wait_data (BSOCK *bsock, int sec);
void bnet_close (BSOCK *bsock);
BSOCK * init_bsock (void *jcr, int sockfd, char *who, char *ip, int port);
BSOCK * dup_bsock (BSOCK *bsock);
char * bnet_strerror (BSOCK *bsock);
char * bnet_sig_to_ascii (BSOCK *bsock);
int bnet_wait_data (BSOCK *bsock, int sec);
+int bnet_wait_data_intr (BSOCK *bsock, int sec);
int bnet_despool (BSOCK *bsock);
int is_bnet_stop (BSOCK *bsock);
int is_bnet_error (BSOCK *bsock);
static char pid_buf[20];
static char btpath[400];
pid_t pid;
+ int exelen = strlen(exepath);
fprintf(stderr, "Kaboom! %s, %s got signal %d. Attempting traceback.\n",
exename, my_name, sig);
- if (strlen(exepath) + 12 > (int)sizeof(btpath)) {
+ if (exelen + 12 > (int)sizeof(btpath)) {
strcpy(btpath, "btraceback");
} else {
strcpy(btpath, exepath);
- strcat(btpath, "/btraceback");
+ if (btpath[exelen-1] != '/') {
+ strcat(btpath, "/btraceback");
+ } else {
+ strcat(btpath, "btraceback");
+ }
+ }
+ if (btpath[exelen-1] != '/') {
+ strcat(exepath, "/");
}
- strcat(exepath, "/");
strcat(exepath, exename);
if (chdir(working_directory) !=0) { /* dump in working directory */
Pmsg2(000, "chdir to %s failed. ERR=%s\n", working_directory, strerror(errno));
argv[1] = exepath; /* path to exe */
argv[2] = pid_buf;
argv[3] = (char *)NULL;
+ fprintf(stderr, "Calling: %s %s %s\n", btpath, exepath, pid_buf);
if (execv(btpath, argv) != 0) {
printf("execv: %s failed: ERR=%s\n", btpath, strerror(errno));
}
default: /* parent */
break;
}
+ /* Parent continue here, waiting for child */
sigdefault.sa_flags = 0;
sigdefault.sa_handler = SIG_DFL;
sigfillset(&sigdefault.sa_mask);
#define TRACE_FILE 1
/* Turn this on if you want to try the new Job semaphore code */
-/* #define USE_SEMAPHORE */
+#define USE_SEMAPHORE
/* IF you undefine this, Bacula will run 10X slower */
#define NO_POLL_TEST 1