Kern's ToDo List
-                   03 August 2004
+                   05 August 2004
 
 Major development:      
 Project                     Developer
 
 1.35 Items to do for release:
 - Knoppix CDROM
-- Add bscan to four-concurrent-jobs regression.
-- Doc new IPv6 syntax
-- Add IPv6 to regression
-- Do tape alerts -- see tapealert.txt
-- Document a get out of jail procedure if everything breaks if 
-  you lost/broke the Catalog -- do the same for "I know my
-  file is there how do I get it back?".
-- When passwords do not match, print message that points the
-  user to the doc.
 - Make Verify jobs require exclusive use of Volume as Restore 
   jobs do.
 - Perhaps add read/write programs and/or plugins to FileSets.
-- Look at adding Client run command that will use the
-  port opened by the client.
-- Fix find_device in stored/dircmd.c:462
-- Add new DCR calling sequences everywhere in SD.
-  This will permit simultaneous use of multiple 
-  devices by a single job.
-- bscan does not put first of two volumes back with all info in
-  bscan-test.
+- Add new DCR calling sequences everywhere in SD. This will permit 
+  simultaneous use of multiple devices by a single job.
 - Fix restore ++++ that get intermingled with "Building directory tree"
 - Solve the termcap.h problem on Solaris configure.
-- Fix ./configure to handle installed SQLite
-- Test Win32 errno handling.
-
 
+- Test Win32 errno handling.
+- Add bscan to four-concurrent-jobs regression.
+- Doc new IPv6 syntax
+- Add IPv6 to regression
+- Alternative to static linking "ldd prog" save all binaries listed,
+  restore them and point LD_LIBRARY_PATH to them.
+- Document a get out of jail procedure if everything breaks if 
+  you lost/broke the Catalog -- do the same for "I know my
+  file is there how do I get it back?".
+- Test Tape Alerts
+- Doc update AllFromVol
+- Doc dbcheck eliminate orphaned clients.
+   
 
 Documentation to do: (any release a little bit at a time)
 - Document query file format.
+- Add more documentation for bsr files.
 - Document problems with Verify and pruning.
 - Document how to use multiple databases.
 - VXA drives have a "cleaning required"
 - Document doing table repair
           
 Testing to do: (painful)
-- Test drive polling!
-- blocksize recognition code.
-- Test if rewind at end of tape waits for tape to rewind.
-- Test cancel at EOM.       
+
 
 For 1.37 Testing/Documentation:
+- Fix find_device in stored/dircmd.c:462 (see code) 
 - Add db check test to regression. Test each function like delete,
   purge, ...
 - If you use restore replace=never, the directory attributes for
   non-existent directories will not be restored properly.
 
 Wish list:  
+- Look at adding Client run command that will use the
+  port opened by the client.
+- bscan does not put first of two volumes back with all info in
+  bscan-test.
 - Implement the FreeBSD nodump flag in chflags.
 - Figure out how to make named console messages go only to that
   console and to the non-restricted console (new console class?).
 - Add better error codes to run_program (10000+)
 - Revisit and revise Disaster Recovery (fix SCSI and RAID 
   disk detection)
+- When passwords do not match, print message that points the
+  user to the doc.
+- Do tape alerts -- see tapealert.txt
+
 
    return 0;
 }
 
+int db_update_media_defaults(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr) 
+{
+   return 1;
+}
+
 
 #endif /* HAVE_BACULA_DB */
 
 
 /* sql.c */
 B_DB *db_init_database(JCR *jcr, const char *db_name, const char *db_user, const char *db_password, 
-                       const char *db_address, int db_port, const char *db_socket);
+                      const char *db_address, int db_port, const char *db_socket);
 int  db_open_database(JCR *jcr, B_DB *db);
 void db_close_database(JCR *jcr, B_DB *db);
 void db_escape_string(char *snew, char *old, int len);
 int db_create_media_record(JCR *jcr, B_DB *db, MEDIA_DBR *media_dbr);
 int db_create_client_record(JCR *jcr, B_DB *db, CLIENT_DBR *cr);
 int db_create_fileset_record(JCR *jcr, B_DB *db, FILESET_DBR *fsr);
-int db_create_pool_record(JCR *jcr, B_DB *db, POOL_DBR *pool_dbr);          
+int db_create_pool_record(JCR *jcr, B_DB *db, POOL_DBR *pool_dbr);         
 int db_create_jobmedia_record(JCR *jcr, B_DB *mdb, JOBMEDIA_DBR *jr);
 int db_create_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr);
 
 int  db_update_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cr);
 int  db_update_pool_record(JCR *jcr, B_DB *db, POOL_DBR *pr);
 int  db_update_media_record(JCR *jcr, B_DB *db, MEDIA_DBR *mr);
+int  db_update_media_defaults(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr);
 int  db_update_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr);
 int  db_add_SIG_to_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, char *SIG, int type);  
 int  db_mark_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t JobId);
 
    return stat;
 }
 
+/* 
+ * Update the Media Record Default values from Pool
+ *
+ * Returns: 0 on failure
+ *         numrows on success
+ */
+int
+db_update_media_defaults(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr) 
+{
+   int stat;
+   char ed1[30], ed2[30], ed3[30];
+       
+
+   db_lock(mdb);
+   if (mr->VolumeName[0]) {
+      Mmsg(&mdb->cmd, "UPDATE Media SET "
+           "Recycle=%d,VolRetention=%s,VolUseDuration=%s,"
+           "MaxVolJobs=%u,MaxVolFiles=%u,MaxVolBytes=%s"
+           " WHERE VolumeName='%s'",
+          mr->Recycle,edit_uint64(mr->VolRetention, ed1), 
+          edit_uint64(mr->VolUseDuration, ed2),
+          mr->MaxVolJobs, mr->MaxVolFiles,
+          edit_uint64(mr->VolBytes, ed3),
+          mr->VolumeName);
+   } else {
+      Mmsg(&mdb->cmd, "UPDATE Media SET "
+           "Recycle=%d,VolRetention=%s,VolUseDuration=%s,"
+           "MaxVolJobs=%u,MaxVolFiles=%u,MaxVolBytes=%s"
+           " WHERE PoolId=%u",
+          mr->Recycle,edit_uint64(mr->VolRetention, ed1), 
+          edit_uint64(mr->VolUseDuration, ed2),
+          mr->MaxVolJobs, mr->MaxVolFiles,
+          edit_uint64(mr->VolBytes, ed3),
+          mr->PoolId);
+   }
+
+   Dmsg1(400, "%s\n", mdb->cmd);
+
+   stat = UPDATE_DB(jcr, mdb, mdb->cmd);
+
+   db_unlock(mdb);
+   return stat;
+}
+
+
 /* 
  * If we have a non-zero InChanger, ensure that no other Media
  *  record has InChanger set on the same Slot.
 
        !cram_md5_auth(dir, password, ssl_need)) {
       stop_bsock_timer(tid);
       sendit( _("Director authorization problem.\n"
-            "Most likely the passwords do not agree.\n"));  
+            "Most likely the passwords do not agree.\n"     
+       "Please see http://www.bacula.org/html-manual/faq.html#AuthorizationErrors for help.\n"));
       return 0;
    }
 
 
    if (!cram_md5_get_auth(sd, jcr->store->password, ssl_need) || 
        !cram_md5_auth(sd, jcr->store->password, ssl_need)) {
       stop_bsock_timer(tid);
-      Jmsg0(jcr, M_FATAL, 0, _("Director and Storage daemon passwords or names not the same.\n"));
+      Jmsg0(jcr, M_FATAL, 0, _("Director and Storage daemon passwords or names not the same.\n"   
+       "Please see http://www.bacula.org/html-manual/faq.html#AuthorizationErrors for help.\n"));
       return 0;
    }
    Dmsg1(116, ">stored: %s", sd->msg);
    if (!cram_md5_get_auth(fd, jcr->client->password, ssl_need) || 
        !cram_md5_auth(fd, jcr->client->password, ssl_need)) {
       stop_bsock_timer(tid);
-      Jmsg(jcr, M_FATAL, 0, _("Director and File daemon passwords or names not the same.\n"));
+      Jmsg(jcr, M_FATAL, 0, _("Director and File daemon passwords or names not the same.\n"   
+       "Please see http://www.bacula.org/html-manual/faq.html#AuthorizationErrors for help.\n"));
       return 0;
    }
    Dmsg1(116, ">filed: %s", fd->msg);
 
 
 /* fd_cmds.c */
 extern int connect_to_file_daemon(JCR *jcr, int retry_interval,
-                                 int max_retry_time, int verbose);
+                                  int max_retry_time, int verbose);
 extern int send_include_list(JCR *jcr);
 extern int send_exclude_list(JCR *jcr);
 extern int send_bootstrap_file(JCR *jcr);
 extern int get_attributes_and_put_in_catalog(JCR *jcr);
 extern int get_attributes_and_compare_to_catalog(JCR *jcr, JobId_t JobId);
 extern int put_file_into_catalog(JCR *jcr, long file_index, char *fname, 
-                         char *link, char *attr, int stream);
+                          char *link, char *attr, int stream);
 extern void get_level_since_time(JCR *jcr, char *since, int since_len);
 extern int send_run_before_and_after_commands(JCR *jcr);
 
 
 /* msgchan.c */
 extern int connect_to_storage_daemon(JCR *jcr, int retry_interval,    
-                             int max_retry_time, int verbose);
+                              int max_retry_time, int verbose);
 extern int start_storage_daemon_job(JCR *jcr);
 extern int start_storage_daemon_message_thread(JCR *jcr);
 extern int bget_dirmsg(BSOCK *bs);
 void free_ua_context(UAContext *ua);
 
 /* ua_select.c */
-STORE  *select_storage_resource(UAContext *ua);
-JOB    *select_job_resource(UAContext *ua);
-JOB    *select_restore_job_resource(UAContext *ua);
-CLIENT *select_client_resource(UAContext *ua);
+STORE   *select_storage_resource(UAContext *ua);
+JOB     *select_job_resource(UAContext *ua);
+JOB     *select_restore_job_resource(UAContext *ua);
+CLIENT  *select_client_resource(UAContext *ua);
 FILESET *select_fileset_resource(UAContext *ua);
-int    select_pool_and_media_dbr(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr);
-int    select_media_dbr(UAContext *ua, MEDIA_DBR *mr);
-int    select_pool_dbr(UAContext *ua, POOL_DBR *pr);
-int    select_client_dbr(UAContext *ua, CLIENT_DBR *cr);
-
-void   start_prompt(UAContext *ua, const char *msg);
-void   add_prompt(UAContext *ua, const char *prompt);
-int    do_prompt(UAContext *ua, const char *automsg, const char *msg, char *prompt, int max_prompt);
-CAT    *get_catalog_resource(UAContext *ua);          
+int     select_pool_and_media_dbr(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr);
+int     select_media_dbr(UAContext *ua, MEDIA_DBR *mr);
+bool    select_pool_dbr(UAContext *ua, POOL_DBR *pr);
+int     select_client_dbr(UAContext *ua, CLIENT_DBR *cr);
+
+void    start_prompt(UAContext *ua, const char *msg);
+void    add_prompt(UAContext *ua, const char *prompt);
+int     do_prompt(UAContext *ua, const char *automsg, const char *msg, char *prompt, int max_prompt);
+CAT    *get_catalog_resource(UAContext *ua);           
 STORE  *get_storage_resource(UAContext *ua, int use_default);
-int    get_media_type(UAContext *ua, char *MediaType, int max_media);
-int    get_pool_dbr(UAContext *ua, POOL_DBR *pr);
-int    get_client_dbr(UAContext *ua, CLIENT_DBR *cr);
+int     get_media_type(UAContext *ua, char *MediaType, int max_media);
+bool    get_pool_dbr(UAContext *ua, POOL_DBR *pr);
+int     get_client_dbr(UAContext *ua, CLIENT_DBR *cr);
 POOL   *get_pool_resource(UAContext *ua);
 POOL   *select_pool_resource(UAContext *ua);
 CLIENT *get_client_resource(UAContext *ua);
-int    get_job_dbr(UAContext *ua, JOB_DBR *jr);
+int     get_job_dbr(UAContext *ua, JOB_DBR *jr);
 
 int find_arg_keyword(UAContext *ua, const char **list);
 int find_arg(UAContext *ua, const char *keyword);
 
 static void update_volfrompool(UAContext *ua, MEDIA_DBR *mr)
 {
    POOL_DBR pr;
-   char VolStatus[50];
 
    memset(&pr, 0, sizeof(pr));
    pr.PoolId = mr->PoolId;
    if (!get_pool_dbr(ua, &pr)) {
       return;
    }
-   bstrncpy(VolStatus, mr->VolStatus, sizeof(VolStatus));
    set_pool_dbr_defaults_in_media_dbr(mr, &pr);
-   bstrncpy(mr->VolStatus, VolStatus, sizeof(mr->VolStatus));
-   if (!db_update_media_record(ua->jcr, ua->db, mr)) {
+   if (!db_update_media_defaults(ua->jcr, ua->db, mr)) {
       bsendmsg(ua, _("Error updating Volume record: ERR=%s"), db_strerror(ua->db));
    } else {
       bsendmsg(ua, _("Volume defaults updated from Pool record.\n"));
    }
 }
 
+/*
+ * Refresh the Volume information from the Pool record
+ *   for all Volumes
+ */
+static void update_all_vols_from_pool(UAContext *ua)
+{
+   POOL_DBR pr;
+   MEDIA_DBR mr;
+
+   memset(&pr, 0, sizeof(pr));
+   if (!get_pool_dbr(ua, &pr)) {
+      return;
+   }
+   memset(&mr, 0, sizeof(mr));
+   set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
+   mr.PoolId = pr.PoolId;
+   if (!db_update_media_defaults(ua->jcr, ua->db, &mr)) {
+      bsendmsg(ua, _("Error updating Volume records: ERR=%s"), db_strerror(ua->db));
+   } else {
+      bsendmsg(ua, _("All Volume defaults updated from Pool record.\n"));
+   }
+}
+
+
 /*
  * Update a media record -- allows you to change the
  *  Volume status. E.g. if you want Bacula to stop
       N_("Recycle"),                  /* 6 */
       N_("Pool"),                     /* 7 */
       N_("FromPool"),                 /* 8 */
+      N_("AllFromPool"),              /* 9 */
       NULL };
 
    for (int i=0; kw[i]; i++) {
       int j;
       POOL_DBR pr;
       if ((j=find_arg_with_value(ua, kw[i])) > 0) {
-        if (!select_media_dbr(ua, &mr)) {
+        if (i != 9 && !select_media_dbr(ua, &mr)) {
            return 0;
         }
         switch (i) {
         case 8:
            update_volfrompool(ua, &mr);
            break;
+        case 9:
+           update_all_vols_from_pool(ua);
         }
         done = true;
       }
 
    }
    memset(&mr, 0, sizeof(mr));
    mr.InChanger = 1;
+   db_lock(ua->db);
    for (int i=1; i<max_slots; i++) {
       if (slot_list[i]) {
         mr.Slot = i;
         /* Set InChanger to zero for this Slot */
-        db_lock(ua->db);
         db_make_inchanger_unique(ua->jcr, ua->db, &mr);
-        db_unlock(ua->db);
-         bsendmsg(ua, _("No VolName for Slot=%d set InChanger to zero.\n"), i);
       }
    }
+   db_unlock(ua->db);
       
 bail_out:
 
 
  *  if error or not found, put up a list of pool DBRs
  *  to choose from.
  *
- *   returns: 0 on error
- *           1 on success and fills in POOL_DBR
+ *   returns: false on error
+ *           true  on success and fills in POOL_DBR
  */
-int get_pool_dbr(UAContext *ua, POOL_DBR *pr)
+bool get_pool_dbr(UAContext *ua, POOL_DBR *pr)
 {
    if (pr->Name[0]) {                /* If name already supplied */
       if (db_get_pool_record(ua->jcr, ua->db, pr) &&
          acl_access_ok(ua, Pool_ACL, pr->Name)) { 
-        return pr->PoolId;
+        return true;
       }
       bsendmsg(ua, _("Could not find Pool \"%s\": ERR=%s"), pr->Name, db_strerror(ua->db));
    }
    if (!select_pool_dbr(ua, pr)) {  /* try once more */
-      return 0;
+      return false;
    }
-   return 1;
+   return true;
 }
 
 /*
  * Select a Pool record from the catalog
  */
-int select_pool_dbr(UAContext *ua, POOL_DBR *pr)
+bool select_pool_dbr(UAContext *ua, POOL_DBR *pr)
 {
    POOL_DBR opr;
    char name[MAX_NAME_LENGTH];
            pr->PoolId = 0;
            break;
         }
-        return 1;
+        return true;
       }
    }
 
    }
    if (num_pools <= 0) {
       bsendmsg(ua, _("No pools defined. Use the \"create\" command to create one.\n"));
-      return 0;
+      return false;
    }
      
    start_prompt(ua, _("Defined Pools:\n"));
    }
    free(ids);
    if (do_prompt(ua, _("Pool"),  _("Select the Pool"), name, sizeof(name)) < 0) {
-      return 0;
+      return false;
    }
    memset(&opr, 0, sizeof(opr));
    bstrncpy(opr.Name, name, sizeof(opr.Name));
 
    if (!db_get_pool_record(ua->jcr, ua->db, &opr)) {
       bsendmsg(ua, _("Could not find Pool \"%s\": ERR=%s"), name, db_strerror(ua->db));
-      return 0;
+      return false;
    }
    memcpy(pr, &opr, sizeof(opr));
-   return 1;
+   return true;
 }
 
 /*
 
    }
    UnlockRes();
    if (!director) {
-      Emsg2(M_FATAL, 0, _("Connection from unknown Director %s at %s rejected.\n"), 
+      Emsg2(M_FATAL, 0, _("Connection from unknown Director %s at %s rejected.\n"   
+       "Please see http://www.bacula.org/html-manual/faq.html#AuthorizationErrors for help.\n"), 
            dirname, bs->who);
       free_pool_memory(dirname);
       return 0;
    btimer_t *tid = start_bsock_timer(bs, 60 * 5);
    if (!cram_md5_auth(bs, director->password, ssl_need) ||
        !cram_md5_get_auth(bs, director->password, ssl_need)) {
-      Emsg1(M_FATAL, 0, _("Incorrect password given by Director at %s.\n"),
+      Emsg1(M_FATAL, 0, _("Incorrect password given by Director at %s.\n"  
+       "Please see http://www.bacula.org/html-manual/faq.html#AuthorizationErrors for help.\n"), 
            bs->who);
       director = NULL;
    }
    stop_bsock_timer(tid);
    memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key));
    if (!stat) {
-      Jmsg(jcr, M_FATAL, 0, _("Authorization key rejected by Storage daemon.\n"));
+      Jmsg(jcr, M_FATAL, 0, _("Authorization key rejected by Storage daemon.\n"   
+       "Please see http://www.bacula.org/html-manual/faq.html#AuthorizationErrors for help.\n"));
    }
    return stat;
 }
 
    if (bfd->fh == INVALID_HANDLE_VALUE) {
       bfd->lerror = GetLastError();
       bfd->berrno = b_errno_win32;
+      errno = b_errno_win32;
       bfd->mode = BF_CLOSED;
    }
    bfd->errmsg = NULL;
              1,                      /* Abort */
              1,                      /* ProcessSecurity */
              &bfd->lpContext)) {     /* Read context */
+        errno = b_errno_win32;
         stat = -1;
       } 
    } else if (bfd->use_backup_api && bfd->mode == BF_WRITE) {
              1,                      /* Abort */
              1,                      /* ProcessSecurity */
              &bfd->lpContext)) {     /* Write context */
+        errno = b_errno_win32;
         stat = -1;
       } 
    }
    if (!CloseHandle(bfd->fh)) {
       stat = -1;
+      errno = b_errno_win32;
    }
    bfd->mode = BF_CLOSED;
    bfd->lpContext = NULL;
 /*
  * Generate error message 
  */
-char *berror(BFILE *bfd)
+/* DO NOT USE */
+char *xberror(BFILE *bfd)
 {
    LPTSTR msg;
 
           &bfd->lpContext)) {          /* Context */
         bfd->lerror = GetLastError();
         bfd->berrno = b_errno_win32;
+        errno = b_errno_win32;
         return -1;
       }
    } else {
           NULL)) {
         bfd->lerror = GetLastError();
         bfd->berrno = b_errno_win32;
+        errno = b_errno_win32;
         return -1;
       }
    }
           &bfd->lpContext)) {          /* Context */
         bfd->lerror = GetLastError();
         bfd->berrno = b_errno_win32;
+        errno = b_errno_win32;
         return -1;
       }
    } else {
           NULL)) {
         bfd->lerror = GetLastError();
         bfd->berrno = b_errno_win32;
+        errno = b_errno_win32;
         return -1;
       }
    }
 
 off_t blseek(BFILE *bfd, off_t offset, int whence)
 {
-   /* ****FIXME**** this is needed if we want to read Win32 Archives */
+   /* ****FIXME**** this must be implemented if we want to read Win32 Archives */
    return -1;
 }
 
    bfd->fid = open(fname, flags, mode);
    bfd->berrno = errno;
    Dmsg1(400, "Open file %d\n", bfd->fid);
+   errno = bfd->berrno;
    return bfd->fid;
 }
 
     return pos;
 }
 
-char *berror(BFILE *bfd)
+/* DO NOT USE */
+char *xberror(BFILE *bfd)
 {
     return strerror(bfd->berrno);
 }
 
 
 enum {
    BF_CLOSED,
-   BF_READ,                          /* BackupRead */
-   BF_WRITE                          /* BackupWrite */
+   BF_READ,                           /* BackupRead */
+   BF_WRITE                           /* BackupWrite */
 };
 
 /* In bfile.c */
 
 /* Basic low level I/O file packet */
 struct BFILE {
-   int use_backup_api;               /* set if using BackupRead/Write */
-   int mode;                         /* set if file is open */
-   HANDLE fh;                        /* Win32 file handle */
-   int fid;                          /* fd if doing Unix style */
-   LPVOID lpContext;                 /* BackupRead/Write context */
-   POOLMEM *errmsg;                  /* error message buffer */
-   DWORD rw_bytes;                   /* Bytes read or written */
-   DWORD lerror;                     /* Last error code */
-   int berrno;                       /* errno */
-};     
+   int use_backup_api;                /* set if using BackupRead/Write */
+   int mode;                          /* set if file is open */
+   HANDLE fh;                         /* Win32 file handle */
+   int fid;                           /* fd if doing Unix style */
+   LPVOID lpContext;                  /* BackupRead/Write context */
+   POOLMEM *errmsg;                   /* error message buffer */
+   DWORD rw_bytes;                    /* Bytes read or written */
+   DWORD lerror;                      /* Last error code */
+   int berrno;                        /* errno */
+};      
 
 HANDLE bget_handle(BFILE *bfd);
 
-#else  /* Linux/Unix systems */
+#else   /* Linux/Unix systems */
 
 /*  =======================================================
  *
 
 /* Basic low level I/O file packet */
 struct BFILE {
-   int fid;                          /* file id on Unix */
+   int fid;                           /* file id on Unix */
    int berrno;
-};     
+};      
 
 #endif
 
-void   binit(BFILE *bfd);
-int    is_bopen(BFILE *bfd);
-int    set_win32_backup(BFILE *bfd);
-int    set_portable_backup(BFILE *bfd);
-int    have_win32_api();
-int    is_portable_backup(BFILE *bfd);
-int    is_stream_supported(int stream);
-int    is_win32_stream(int stream);
-char   *berror(BFILE *bfd);
-int    bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
-int    bclose(BFILE *bfd);
+void    binit(BFILE *bfd);
+int     is_bopen(BFILE *bfd);
+int     set_win32_backup(BFILE *bfd);
+int     set_portable_backup(BFILE *bfd);
+int     have_win32_api();
+int     is_portable_backup(BFILE *bfd);
+int     is_stream_supported(int stream);
+int     is_win32_stream(int stream);
+char   *xberror(BFILE *bfd);          /* DO NOT USE  -- use berrno class */
+int     bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
+int     bclose(BFILE *bfd);
 ssize_t bread(BFILE *bfd, void *buf, size_t count);
 ssize_t bwrite(BFILE *bfd, void *buf, size_t count);
-off_t  blseek(BFILE *bfd, off_t offset, int whence);
+off_t   blseek(BFILE *bfd, off_t offset, int whence);
 const char   *stream_to_ascii(int stream);
 
 #endif /* __BFILE_H */
 
    case FT_NOFOLLOW:
    case FT_NOSTAT:
    case FT_NOCHG:
-   case FT_DIRNOCHG:
    case FT_ISARCH:
    case FT_NORECURSE:
    case FT_NOFSCHG:
    case FT_RAW:
    case FT_FIFO:
    case FT_SPEC:
+   case FT_DIRNOCHG:
       if (accept_file(ff)) {
         return ff->callback(ff, hpkt);
       } else {
 
       stop_bsock_timer(tid);
       printf(_("%s: Director authorization problem.\n"), my_name);
       set_text(_("Director authorization problem.\n"), -1);
+      set_text(_(
+       "Please see http://www.bacula.org/html-manual/faq.html#AuthorizationErrors for help.\n"), 
+       -1);
       return 0;
    }
 
 
            dev_name(dev), NPRT(jcr->VolumeName));
       Jmsg2(jcr, M_ERROR, 0, _("num_writers=%d state=%x\n"), dev->num_writers, dev->state);
    }
-// detach_jcr_from_device(dev, jcr);
+
+   /* Fire off Alert command and include any output */
+   if (jcr->device->alert_command) {
+      POOLMEM *alert;
+      int status;
+      BPIPE *bpipe;
+      char line[MAXSTRING];
+      alert = get_pool_memory(PM_FNAME);
+      alert = edit_device_codes(jcr, alert, jcr->device->alert_command, "");
+      bpipe = open_bpipe(alert, 0, "r");
+      free_pool_memory(alert);
+      while (fgets(line, sizeof(line), bpipe->rfd)) {
+         Jmsg(jcr, M_INFO, 0, _("Alert: %s"), line);
+      }
+      status = close_bpipe(bpipe);
+      if (status != 0) {
+        berrno be;
+        be.set_errno(status);
+         Jmsg(jcr, M_INFO, 0, _("3997 Bad alert command: %s: ERR=%s.\n"),
+             alert, be.strerror());
+      }
+
+      Dmsg1(400, "alert status=%d\n", status);
+      
+   }
    if (dev->prev && !dev_state(dev, ST_READ) && !dev->num_writers) {
       P(mutex);
       unlock_device(dev);
 
    }
    UnlockRes();
    if (!director) {
-      Emsg2(M_FATAL, 0, _("Connection from unknown Director %s at %s rejected.\n"), 
+      Emsg2(M_FATAL, 0, _("Connection from unknown Director %s at %s rejected.\n"   
+       "Please see http://www.bacula.org/html-manual/faq.html#AuthorizationErrors for help.\n"), 
            dirname, bs->who);
       free_pool_memory(dirname);
       return 0;
    if (!cram_md5_auth(bs, director->password, ssl_need) ||
        !cram_md5_get_auth(bs, director->password, ssl_need)) {
       stop_bsock_timer(tid);
-      Emsg0(M_FATAL, 0, _("Incorrect password given by Director.\n"));
+      Emsg0(M_FATAL, 0, _("Incorrect password given by Director.\n"   
+       "Please see http://www.bacula.org/html-manual/faq.html#AuthorizationErrors for help.\n"));
       free_pool_memory(dirname);
       return 0;
    }
    }
    stop_bsock_timer(tid);
    if (!jcr->authenticated) {
-      Jmsg(jcr, M_FATAL, 0, _("Incorrect authorization key from File daemon at %s rejected.\n"), 
+      Jmsg(jcr, M_FATAL, 0, _("Incorrect authorization key from File daemon at %s rejected.\n"
+       "Please see http://www.bacula.org/html-manual/faq.html#AuthorizationErrors for help.\n"),
           fd->who);
    }
    return jcr->authenticated;
 
 
       case 'e':                    /* exclude list */
          if ((fd = fopen(optarg, "r")) == NULL) {
+           berrno be;
             Pmsg2(0, "Could not open exclude file: %s, ERR=%s\n",
-              optarg, strerror(errno));
+              optarg, be.strerror());
            exit(1);
         }
         while (fgets(line, sizeof(line), fd) != NULL) {
 
       case 'i':                    /* include list */
          if ((fd = fopen(optarg, "r")) == NULL) {
+           berrno be;
             Pmsg2(0, "Could not open include file: %s, ERR=%s\n",
-              optarg, strerror(errno));
+              optarg, be.strerror());
            exit(1);
         }
         while (fgets(line, sizeof(line), fd) != NULL) {
 
    /* Make sure where directory exists and that it is a directory */
    if (stat(where, &statp) < 0) {
+      berrno be;
       Emsg2(M_ERROR_TERM, 0, "Cannot stat %s. It must exist. ERR=%s\n",
-        where, strerror(errno));
+        where, be.strerror());
    }
    if (!S_ISDIR(statp.st_mode)) {
       Emsg1(M_ERROR_TERM, 0, "%s must be a directory.\n", where);
            if (fileAddr != faddr) {
               fileAddr = faddr;
               if (blseek(&bfd, (off_t)fileAddr, SEEK_SET) < 0) {
+                 berrno be;
                   Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"), 
-                    attr->ofname, strerror(errno));
+                    attr->ofname, be.strerror());
               }
            }
         } else {
         total += wsize;
          Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
         if ((uint32_t)bwrite(&bfd, wbuf, wsize) != wsize) {
+           berrno be;
             Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"), 
-              attr->ofname, strerror(errno));
+              attr->ofname, be.strerror());
         }
         fileAddr += wsize;
       }
            if (fileAddr != faddr) {
               fileAddr = faddr;
               if (blseek(&bfd, (off_t)fileAddr, SEEK_SET) < 0) {
+                 berrno be;
                   Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"), 
-                    edit_uint64(fileAddr, ec1), attr->ofname, berror(&bfd));
+                    edit_uint64(fileAddr, ec1), attr->ofname, be.strerror());
                  extract = false;
                  return true;
               }
 
          Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
         if ((uLongf)bwrite(&bfd, compress_buf, (size_t)compress_len) != compress_len) {
+           berrno be;
             Pmsg0(0, "===Write error===\n");
             Emsg2(M_ERROR, 0, _("Write error on %s: %s\n"), 
-              attr->ofname, strerror(errno));
+              attr->ofname, be.strerror());
            extract = false;
            return true;
         }
 
 
 /* External subroutines */
 extern void free_config_resources();
-extern char *edit_device_codes(JCR *jcr, char *omsg, const char *imsg, const char *cmd);
 
 /* Exported variables */
 int quit = 0;
 
 
 static DEVICE *find_device(JCR *jcr, char *dname)
 {
-   DEVRES *device = NULL;
+   DEVRES *device;
    bool found = false;
 
    unbash_spaces(dname);
    LockRes();
-   while ((device=(DEVRES *)GetNextRes(R_DEVICE, (RES *)device))) {
+   foreach_res(device, R_DEVICE) {
       /* Find resource, and make sure we were able to open it */
       if (strcmp(device->hdr.name, dname) == 0 && device->dev) {
          Dmsg1(20, "Found device %s\n", device->hdr.name);
 
 int     autoload_device(DCR *dcr, int writing, BSOCK *dir);
 bool    autochanger_list(DCR *dcr, BSOCK *dir);
 void    invalidate_slot_in_catalog(DCR *dcr);
+char   *edit_device_codes(JCR *jcr, char *omsg, const char *imsg, const char *cmd);
 
 
 /* From parse_bsr.c */
 
    {"closeonpoll",           store_yesno,  ITEM(res_dev.cap_bits), CAP_CLOSEONPOLL, ITEM_DEFAULT, 0},
    {"changerdevice",         store_strname,ITEM(res_dev.changer_name), 0, 0, 0},
    {"changercommand",        store_strname,ITEM(res_dev.changer_command), 0, 0, 0},
+   {"alertcommand",          store_strname,ITEM(res_dev.alert_command), 0, 0, 0},
    {"maximumchangerwait",    store_pint,   ITEM(res_dev.max_changer_wait), 0, ITEM_DEFAULT, 5 * 60},
    {"maximumopenwait",       store_pint,   ITEM(res_dev.max_open_wait), 0, ITEM_DEFAULT, 5 * 60},
    {"maximumopenvolumes",    store_pint,   ITEM(res_dev.max_open_vols), 0, ITEM_DEFAULT, 1},
         if (res->res_dev.changer_command) {
            free(res->res_dev.changer_command);
         }
+        if (res->res_dev.alert_command) {
+           free(res->res_dev.alert_command);
+        }
         if (res->res_dev.spool_directory) {
            free(res->res_dev.spool_directory);
         }
 
    char *device_name;                /* Archive device name */
    char *changer_name;               /* Changer device name */
    char *changer_command;            /* Changer command  -- external program */
+   char *alert_command;              /* Alert command -- external program */
    char *spool_directory;            /* Spool file directory */
    uint32_t drive_index;             /* Autochanger drive index */
    uint32_t cap_bits;                /* Capabilities of this device */
 
 static NAME_LIST name_list;
 static char buf[2000];
 
-#define MAX_ID_LIST_LEN 1000000
+#define MAX_ID_LIST_LEN 10000000
 
 /* Forward referenced functions */
 static int make_id_list(const char *query, ID_LIST *id_list);
 static void eliminate_orphaned_path_records();
 static void eliminate_orphaned_filename_records();
 static void eliminate_orphaned_fileset_records();
+static void eliminate_orphaned_client_records();
 static void repair_bad_paths();
 static void repair_bad_filenames();
 static void do_interactive_mode();
       eliminate_orphaned_path_records();
       eliminate_orphaned_filename_records();
       eliminate_orphaned_fileset_records();
+      eliminate_orphaned_client_records();
    } else {
       do_interactive_mode();
    }
      9) Eliminate orphaned Path records\n\
     10) Eliminate orphaned Filename records\n\
     11) Eliminate orphaned FileSet records\n\
-    12) All (3-11)\n\
-    13) Quit\n"));
+    12) Eliminate orphaned Client records\n\
+    13) All (3-12)\n\
+    14) Quit\n"));
        } else {
          printf(_("\n\
      1) Toggle modify database flag\n\
      9) Check for orphaned Path records\n\
     10) Check for orphaned Filename records\n\
     11) Check for orphaned FileSet records\n\
-    12) All (3-11)\n\
-    13) Quit\n"));
+    12) Check for orphaned FileSet records\n\
+    13) All (3-12)\n\
+    14) Quit\n"));
        }
 
       cmd = get_cmd(_("Select function number: "));
            eliminate_orphaned_fileset_records();
            break;
         case 12:
+           eliminate_orphaned_client_records();
+           break;
+        case 13:
            repair_bad_filenames();
            repair_bad_paths();
            eliminate_duplicate_filenames();
            eliminate_orphaned_path_records();
            eliminate_orphaned_filename_records();
            eliminate_orphaned_fileset_records();
+           eliminate_orphaned_client_records();
            break;
-        case 13:
+        case 14:
            quit = true;
            break;
         }
    return 0;
 }
 
+static int print_client_handler(void *ctx, int num_fields, char **row)
+{
+   printf(_("Orphaned ClientId=%s Name=\"%s\"\n"), 
+             NPRT(row[0]), NPRT(row[1]));
+   return 0;
+}
+
+
   
 /*
  * Called here with each id to be added to the list
    }
 }
 
+static void eliminate_orphaned_client_records()
+{
+   const char *query;
+
+   printf("Checking for orphaned Client entries.\n");
+   /* In English:
+    *  Wiffle through Client for every Client
+    *  joining with the Job table including every Client even if
+    *  there is not a match in Job (left outer join), then
+    *  filter out only those where no Job points to a Client
+    *  i.e. Job.Client is NULL
+    */
+   query = "SELECT Client.ClientId,Client.Name FROM Client "
+           "LEFT OUTER JOIN Job ON (Client.ClientId=Job.ClientId) "
+           "WHERE Job.ClientId IS NULL";
+   if (verbose > 1) {
+      printf("%s\n", query);
+   }
+   if (!make_id_list(query, &id_list)) {
+      exit(1);
+   }
+   printf("Found %d orphaned Client records.\n", id_list.num_ids);
+   if (id_list.num_ids && verbose && yes_no("Print them? (yes/no): ")) {
+      for (int i=0; i < id_list.num_ids; i++) {
+         sprintf(buf, "SELECT ClientId,Name FROM Client "
+                      "WHERE ClientId=%u", id_list.Id[i]);
+        if (!db_sql_query(db, buf, print_client_handler, NULL)) {
+            printf("%s\n", db_strerror(db));
+        }
+      }
+   }
+   
+   if (fix && id_list.num_ids > 0) {
+      printf("Deleting %d orphaned Client records.\n", id_list.num_ids);
+      delete_id_list("DELETE FROM Client WHERE ClientId=%u", &id_list);
+   }
+}
+
+
 static void repair_bad_filenames()
 {
    const char *query;
 
 /* */
 #undef  VERSION
 #define VERSION "1.35.1"
-#define BDATE   "30 July 2004"
-#define LSMDATE "30Jul04"
+#define BDATE   "05 August 2004"
+#define LSMDATE "05Aug04"
 
 /* Debug flags */
 #undef  DEBUG
 
        !cram_md5_auth(dir, password, ssl_need)) {
       stop_bsock_timer(tid);
       csprint("Director authorization problem.\nMost likely the passwords do not agree.\n", CS_DATA);
+      csprint(
+       "Please see http://www.bacula.org/html-manual/faq.html#AuthorizationErrors for help.\n", CS_DATA);
       return 0;
    }