X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fdird%2Fdird_conf.c;h=da85d7ae23006a9371aca0a96eae90b0a6fb4bf4;hb=86da6147f63b29cb85d51620b55bae7266f1c890;hp=5cad2b9d6b0e8eb7d51c6dba7531bbf2a0f17d68;hpb=a00e029e3ea1f4fa1d98a3bf3402809844a493c9;p=bacula%2Fbacula diff --git a/bacula/src/dird/dird_conf.c b/bacula/src/dird/dird_conf.c index 5cad2b9d6b..da85d7ae23 100644 --- a/bacula/src/dird/dird_conf.c +++ b/bacula/src/dird/dird_conf.c @@ -1,7 +1,7 @@ /* Bacula(R) - The Network Backup Solution - Copyright (C) 2000-2016 Kern Sibbald + Copyright (C) 2000-2017 Kern Sibbald The original author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. @@ -11,7 +11,7 @@ Public License, v3.0 ("AGPLv3") and some additional permissions and terms pursuant to its AGPLv3 Section 7. - This notice must be preserved when any source code is + This notice must be preserved when any source code is conveyed and/or propagated. Bacula(R) is a registered trademark of Kern Sibbald. @@ -36,7 +36,6 @@ * for the resource records. * * Kern Sibbald, January MM - * */ @@ -49,8 +48,14 @@ */ int32_t r_first = R_FIRST; int32_t r_last = R_LAST; -static RES *sres_head[R_LAST - R_FIRST + 1]; -RES **res_head = sres_head; +RES_HEAD **res_head; + +static pthread_mutex_t globals_mutex = PTHREAD_MUTEX_INITIALIZER; +dlist client_globals; +dlist job_globals; +dlist store_globals; +dlist sched_globals; + /* Imported subroutines */ extern void store_run(LEX *lc, RES_ITEM *item, int index, int pass); @@ -65,7 +70,8 @@ void store_level(LEX *lc, RES_ITEM *item, int index, int pass); void store_replace(LEX *lc, RES_ITEM *item, int index, int pass); void store_acl(LEX *lc, RES_ITEM *item, int index, int pass); void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass); -static void store_device(LEX *lc, RES_ITEM *item, int index, int pass); +void store_ac_res(LEX *lc, RES_ITEM *item, int index, int pass); +void store_device(LEX *lc, RES_ITEM *item, int index, int pass); void store_actiononpurge(LEX *lc, RES_ITEM *item, int index, int pass); static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass); static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass); @@ -85,10 +91,240 @@ URES res_all; #endif int32_t res_all_size = sizeof(res_all); +/* Implementation of certain classes */ + +void CLIENT::create_client_globals() +{ + globals = (CLIENT_GLOBALS *)malloc(sizeof(CLIENT_GLOBALS)); + memset(globals, 0, sizeof(CLIENT_GLOBALS)); + globals->name = bstrdup(name()); + client_globals.append(globals); +} + +int32_t CLIENT::getNumConcurrentJobs() +{ + if (!globals) { + return 0; + } + return globals->NumConcurrentJobs; +} + +void CLIENT::setNumConcurrentJobs(int32_t num) +{ + P(globals_mutex); + if (!globals) { + create_client_globals(); + /* Copy .conf IP address and Enabled */ + globals->enabled = Enabled; + globals->SetIPaddress = bstrdup(client_address); + } + globals->NumConcurrentJobs = num; + V(globals_mutex); + ASSERT(num >= 0); + Dmsg2(200, "Set NumConcurrentJobs=%ld for Client %s\n", + num, globals->name); +} + +char *CLIENT::address() +{ + if (!globals) { + return client_address; + } + if (!globals->SetIPaddress) { + return client_address; + } + return globals->SetIPaddress; +} + +void CLIENT::setAddress(char *addr) +{ + P(globals_mutex); + if (!globals) { + create_client_globals(); + globals->enabled = Enabled; /* copy .conf variable */ + } + if (globals->SetIPaddress) { + free(globals->SetIPaddress); + } + globals->SetIPaddress = bstrdup(addr); + V(globals_mutex); +} + +bool CLIENT::is_enabled() +{ + if (!globals) { + return Enabled; + } + return globals->enabled; +} + +void CLIENT::setEnabled(bool val) +{ + P(globals_mutex); + if (!globals) { + create_client_globals(); + globals->SetIPaddress = bstrdup(client_address); /* copy .conf variable */ + } + globals->enabled = val; + V(globals_mutex); + Dmsg2(200, "Set Enabled=%d for Client %s\n", + val, globals->name); +} + +void JOB::create_job_globals() +{ + globals = (JOB_GLOBALS *)malloc(sizeof(JOB_GLOBALS)); + memset(globals, 0, sizeof(JOB_GLOBALS)); + globals->name = bstrdup(name()); + job_globals.append(globals); +} + +int32_t JOB::getNumConcurrentJobs() +{ + if (!globals) { + return 0; + } + return globals->NumConcurrentJobs; +} + +void JOB::setNumConcurrentJobs(int32_t num) +{ + P(globals_mutex); + if (!globals) { + create_job_globals(); + globals->enabled = Enabled; /* copy .conf variable */ + } + globals->NumConcurrentJobs = num; + V(globals_mutex); + ASSERT(num >= 0); + Dmsg2(200, "Set NumConcurrentJobs=%ld for Job %s\n", + num, globals->name); +} + +bool JOB::is_enabled() +{ + if (!globals) { + return Enabled; + } + return globals->enabled; +} + +void JOB::setEnabled(bool val) +{ + P(globals_mutex); + if (!globals) { + create_job_globals(); + } + globals->enabled = val; + V(globals_mutex); + Dmsg2(200, "Set Enabled=%d for Job %s\n", + val, globals->name); +} + +void STORE::create_store_globals() +{ + globals = (STORE_GLOBALS *)malloc(sizeof(STORE_GLOBALS)); + memset(globals, 0, sizeof(STORE_GLOBALS)); + globals->name = bstrdup(name()); + store_globals.append(globals); +} + +int32_t STORE::getNumConcurrentReadJobs() +{ + if (!globals) { + return 0; + } + return globals->NumConcurrentReadJobs; +} + +void STORE::setNumConcurrentReadJobs(int32_t num) +{ + P(globals_mutex); + if (!globals) { + create_store_globals(); + globals->enabled = Enabled; /* copy .conf variable */ + } + globals->NumConcurrentReadJobs = num; + V(globals_mutex); + Dmsg2(200, "Set NumConcurrentReadJobs=%ld for Store %s\n", + num, globals->name); + ASSERT(num >= 0); +} + +int32_t STORE::getNumConcurrentJobs() +{ + if (!globals) { + return 0; + } + return globals->NumConcurrentJobs; +} + +void STORE::setNumConcurrentJobs(int32_t num) +{ + P(globals_mutex); + if (!globals) { + create_store_globals(); + globals->enabled = Enabled; /* copy .conf variable */ + } + globals->NumConcurrentJobs = num; + V(globals_mutex); + Dmsg2(200, "Set numconcurrentJobs=%ld for Store %s\n", + num, globals->name); + ASSERT(num >= 0); +} + +bool STORE::is_enabled() +{ + if (!globals) { + return Enabled; + } + return globals->enabled; +} + +void STORE::setEnabled(bool val) +{ + P(globals_mutex); + if (!globals) { + create_store_globals(); + } + globals->enabled = val; + V(globals_mutex); + Dmsg2(200, "Set Enabled=%d for Storage %s\n", + val, globals->name); +} + +void SCHED::create_sched_globals() +{ + globals = (SCHED_GLOBALS *)malloc(sizeof(CLIENT_GLOBALS)); + memset(globals, 0, sizeof(SCHED_GLOBALS)); + globals->name = bstrdup(name()); + sched_globals.append(globals); +} + +bool SCHED::is_enabled() +{ + if (!globals) { + return Enabled; + } + return globals->enabled; +} + +void SCHED::setEnabled(bool val) +{ + P(globals_mutex); + if (!globals) { + create_sched_globals(); + } + globals->enabled = val; + V(globals_mutex); + Dmsg2(200, "Set Enabled=%d for Schedule %s\n", + val, globals->name); +} + /* * Definition of records permitted within each * resource with the routine to process the record - * information. + * information. NOTE! quoted names must be in lower case. */ /* * Director Resource @@ -109,7 +345,7 @@ static RES_ITEM dir_items[] = { {"ScriptsDirectory", store_dir, ITEM(res_dir.scripts_directory), 0, 0, 0}, {"PidDirectory", store_dir, ITEM(res_dir.pid_directory), 0, ITEM_REQUIRED, 0}, {"SubsysDirectory", store_dir, ITEM(res_dir.subsys_directory), 0, 0, 0}, - {"MaximumConcurrentJobs", store_pint32, ITEM(res_dir.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1}, + {"MaximumConcurrentJobs", store_pint32, ITEM(res_dir.MaxConcurrentJobs), 0, ITEM_DEFAULT, 20}, {"MaximumReloadRequests", store_pint32, ITEM(res_dir.MaxReload), 0, ITEM_DEFAULT, 32}, {"MaximumConsoleConnections", store_pint32, ITEM(res_dir.MaxConsoleConnect), 0, ITEM_DEFAULT, 20}, {"Password", store_password, ITEM(res_dir.password), 0, ITEM_REQUIRED, 0}, @@ -128,6 +364,7 @@ static RES_ITEM dir_items[] = { {"TlsAllowedCn", store_alist_str, ITEM(res_dir.tls_allowed_cns), 0, 0, 0}, {"StatisticsRetention", store_time, ITEM(res_dir.stats_retention), 0, ITEM_DEFAULT, 60*60*24*31*12*5}, {"VerId", store_str, ITEM(res_dir.verid), 0, 0, 0}, + {"CommCompression", store_bool, ITEM(res_dir.comm_compression), 0, ITEM_DEFAULT, true}, {NULL, NULL, {0}, 0, 0, 0} }; @@ -150,6 +387,8 @@ static RES_ITEM con_items[] = { {"FilesetAcl", store_acl, ITEM(res_con.ACL_lists), FileSet_ACL, 0, 0}, {"CatalogAcl", store_acl, ITEM(res_con.ACL_lists), Catalog_ACL, 0, 0}, {"WhereAcl", store_acl, ITEM(res_con.ACL_lists), Where_ACL, 0, 0}, + {"RestoreClientAcl", store_acl, ITEM(res_con.ACL_lists), RestoreClient_ACL, 0, 0}, + {"BackupClientAcl", store_acl, ITEM(res_con.ACL_lists), BackupClient_ACL, 0, 0}, {"PluginOptionsAcl", store_acl, ITEM(res_con.ACL_lists), PluginOptions_ACL, 0, 0}, {"TlsAuthenticate", store_bool, ITEM(res_con.tls_authenticate), 0, 0, 0}, {"TlsEnable", store_bool, ITEM(res_con.tls_enable), 0, 0, 0}, @@ -174,8 +413,8 @@ static RES_ITEM con_items[] = { static RES_ITEM cli_items[] = { {"Name", store_name, ITEM(res_client.hdr.name), 0, ITEM_REQUIRED, 0}, {"Description", store_str, ITEM(res_client.hdr.desc), 0, 0, 0}, - {"fdaddress", store_str, ITEM(res_client.address), 0, 0, 0}, - {"Address", store_str, ITEM(res_client.address), 0, ITEM_REQUIRED, 0}, + {"fdaddress", store_str, ITEM(res_client.client_address), 0, 0, 0}, + {"Address", store_str, ITEM(res_client.client_address), 0, ITEM_REQUIRED, 0}, {"FdPort", store_pint32, ITEM(res_client.FDport), 0, ITEM_DEFAULT, 9102}, {"fdpassword", store_password, ITEM(res_client.password), 0, 0, 0}, {"Password", store_password, ITEM(res_client.password), 0, ITEM_REQUIRED, 0}, @@ -197,7 +436,7 @@ static RES_ITEM cli_items[] = { {"TlsKey", store_dir, ITEM(res_client.tls_keyfile), 0, 0, 0}, {"TlsAllowedCn", store_alist_str, ITEM(res_client.tls_allowed_cns), 0, 0, 0}, {"MaximumBandwidthPerJob", store_speed, ITEM(res_client.max_bandwidth), 0, 0, 0}, - {"Enabled", store_bool, ITEM(res_client.enabled), 0, ITEM_DEFAULT, true}, + {"Enabled", store_bool, ITEM(res_client.Enabled), 0, ITEM_DEFAULT, true}, {NULL, NULL, {0}, 0, 0, 0} }; @@ -216,8 +455,14 @@ static RES_ITEM store_items[] = { {"Password", store_password, ITEM(res_store.password), 0, ITEM_REQUIRED, 0}, {"Device", store_device, ITEM(res_store.device), R_DEVICE, ITEM_REQUIRED, 0}, {"MediaType", store_strname, ITEM(res_store.media_type), 0, ITEM_REQUIRED, 0}, + /* _bool, + * Big kludge, these two autochanger definitions must be in + * this order and together. + */ + {"Autochanger", store_ac_res, ITEM(res_store.changer), 0, ITEM_DEFAULT, 0}, {"Autochanger", store_bool, ITEM(res_store.autochanger), 0, ITEM_DEFAULT, false}, - {"Enabled", store_bool, ITEM(res_store.enabled), 0, ITEM_DEFAULT, true}, + {"SharedStorage", store_ac_res, ITEM(res_store.shared_storage), 1, ITEM_DEFAULT, 0}, + {"Enabled", store_bool, ITEM(res_store.Enabled), 0, ITEM_DEFAULT, true}, {"AllowCompression", store_bool, ITEM(res_store.AllowCompress), 0, ITEM_DEFAULT, true}, {"HeartbeatInterval", store_time, ITEM(res_store.heartbeat_interval), 0, ITEM_DEFAULT, 5 * 60}, {"MaximumConcurrentJobs", store_pint32, ITEM(res_store.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1}, @@ -251,12 +496,11 @@ static RES_ITEM cat_items[] = { {"User", store_str, ITEM(res_cat.db_user), 0, 0, 0}, {"DbName", store_str, ITEM(res_cat.db_name), 0, ITEM_REQUIRED, 0}, {"dbdriver", store_str, ITEM(res_cat.db_driver), 0, 0, 0}, - {"DbSocket", store_str, ITEM(res_cat.db_socket), 0, 0, 0}, {"dbsslkey", store_str, ITEM(res_cat.db_ssl_key), 0, 0, 0}, {"dbsslcert", store_str, ITEM(res_cat.db_ssl_cert), 0, 0, 0}, {"dbsslca", store_str, ITEM(res_cat.db_ssl_ca), 0, 0, 0}, {"dbsslcapath", store_str, ITEM(res_cat.db_ssl_capath), 0, 0, 0}, - {"dbsslcipher", store_str, ITEM(res_cat.db_ssl_cipher), 0, 0, 0}, + {"DbSocket", store_str, ITEM(res_cat.db_socket), 0, 0, 0}, /* Turned off for the moment */ {"MultipleConnections", store_bit, ITEM(res_cat.mult_db_connections), 0, 0, 0}, {"DisableBatchInsert", store_bool, ITEM(res_cat.disable_batch_insert), 0, ITEM_DEFAULT, false}, @@ -318,20 +562,24 @@ RES_ITEM job_items[] = { {"PruneFiles", store_bool, ITEM(res_job.PruneFiles), 0, ITEM_DEFAULT, false}, {"PruneVolumes",store_bool, ITEM(res_job.PruneVolumes), 0, ITEM_DEFAULT, false}, {"PurgeMigrationJob", store_bool, ITEM(res_job.PurgeMigrateJob), 0, ITEM_DEFAULT, false}, - {"Enabled", store_bool, ITEM(res_job.enabled), 0, ITEM_DEFAULT, true}, + {"Enabled", store_bool, ITEM(res_job.Enabled), 0, ITEM_DEFAULT, true}, {"SnapshotRetention", store_time, ITEM(res_job.SnapRetention), 0, ITEM_DEFAULT, 0}, {"SpoolAttributes",store_bool, ITEM(res_job.SpoolAttributes), 0, ITEM_DEFAULT, true}, {"SpoolData", store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false}, {"SpoolSize", store_size64, ITEM(res_job.spool_size), 0, 0, 0}, {"ReRunFailedLevels", store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false}, {"PreferMountedVolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true}, - {"RunBeforeJob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0}, - {"RunAfterJob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0}, - {"RunAfterFailedJob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0}, - {"ClientRunBeforeJob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0}, - {"ClientRunAfterJob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0}, - {"ConsoleRunBeforeJob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0}, - {"ConsoleRunAfterJob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0}, + /* + * JSON tools skip Directive in lowercase. They are deprecated or + * are synonym with an other one that follows. Like User and dbuser. + */ + {"runbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0}, + {"runafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0}, + {"runafterfailedjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0}, + {"clientrunbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0}, + {"clientrunafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0}, + {"consolerunbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0}, + {"consolerunafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0}, {"Runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0}, {"MaximumConcurrentJobs", store_pint32, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1}, {"MaximumSpawnedJobs", store_pint32, ITEM(res_job.MaxSpawnedJobs), 0, ITEM_DEFAULT, 600}, @@ -340,6 +588,7 @@ RES_ITEM job_items[] = { {"RescheduleInterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30}, {"RescheduleTimes", store_pint32, ITEM(res_job.RescheduleTimes), 0, 0, 0}, {"Priority", store_pint32, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10}, + {"BackupsToKeep", store_pint32, ITEM(res_job.BackupsToKeep), 0, ITEM_DEFAULT, 0}, {"AllowMixedPriority", store_bool, ITEM(res_job.allow_mixed_priority), 0, ITEM_DEFAULT, false}, {"WritePartAfterJob", store_bool, ITEM(res_job.write_part_after_job), 0, ITEM_DEFAULT, true}, {"SelectionPattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0}, @@ -350,6 +599,7 @@ RES_ITEM job_items[] = { {"CancelLowerLevelDuplicates", store_bool, ITEM(res_job.CancelLowerLevelDuplicates), 0, ITEM_DEFAULT, false}, {"CancelQueuedDuplicates", store_bool, ITEM(res_job.CancelQueuedDuplicates), 0, ITEM_DEFAULT, false}, {"CancelRunningDuplicates", store_bool, ITEM(res_job.CancelRunningDuplicates), 0, ITEM_DEFAULT, false}, + {"DeleteConsolidatedJobs", store_bool, ITEM(res_job.DeleteConsolidatedJobs), 0, ITEM_DEFAULT, false}, {"PluginOptions", store_str, ITEM(res_job.PluginOptions), 0, 0, 0}, {"Base", store_alist_res, ITEM(res_job.base), R_JOB, 0, 0}, {NULL, NULL, {0}, 0, 0, 0} @@ -379,7 +629,7 @@ static RES_ITEM sch_items[] = { {"Name", store_name, ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0}, {"Description", store_str, ITEM(res_sch.hdr.desc), 0, 0, 0}, {"Run", store_run, ITEM(res_sch.run), 0, 0, 0}, - {"Enabled", store_bool, ITEM(res_sch.enabled), 0, ITEM_DEFAULT, true}, + {"Enabled", store_bool, ITEM(res_sch.Enabled), 0, ITEM_DEFAULT, true}, {NULL, NULL, {0}, 0, 0, 0} }; @@ -405,6 +655,7 @@ static RES_ITEM pool_items[] = { {"MaximumVolumeFiles", store_pint32, ITEM(res_pool.MaxVolFiles), 0, 0, 0}, {"MaximumVolumeBytes", store_size64, ITEM(res_pool.MaxVolBytes), 0, 0, 0}, {"CatalogFiles", store_bool, ITEM(res_pool.catalog_files), 0, ITEM_DEFAULT, true}, + {"CacheRetention", store_time, ITEM(res_pool.CacheRetention), 0, 0, 0}, {"VolumeRetention", store_time, ITEM(res_pool.VolRetention), 0, ITEM_DEFAULT, 60*60*24*365}, {"VolumeUseDuration", store_time, ITEM(res_pool.VolUseDuration), 0, 0, 0}, {"MigrationTime", store_time, ITEM(res_pool.MigrationTime), 0, 0, 0}, @@ -465,6 +716,7 @@ RES_TABLE resources[] = { {"Console", con_items, R_CONSOLE}, {"JobDefs", job_items, R_JOBDEFS}, {"Device", NULL, R_DEVICE}, /* info obtained from SD */ + {"Autochanger", store_items, R_AUTOCHANGER}, /* alias for R_STORAGE */ {NULL, NULL, 0} }; @@ -532,7 +784,7 @@ s_jt migtypes[] = { /* Options permitted in Restore replace= */ -struct s_kw ReplaceOptions[] = { +s_kw ReplaceOptions[] = { {"Always", REPLACE_ALWAYS}, {"IfNewer", REPLACE_IFNEWER}, {"IfOlder", REPLACE_IFOLDER}, @@ -569,6 +821,7 @@ const char *level_to_str(int level) /* Dump contents of resource */ void dump_resource(int type, RES *ares, void sendit(void *sock, const char *fmt, ...), void *sock) { + RES *next; URES *res = (URES *)ares; bool recurse = true; char ed1[100], ed2[100], ed3[100]; @@ -619,13 +872,13 @@ void dump_resource(int type, RES *ares, void sendit(void *sock, const char *fmt, break; case R_CLIENT: - if (!acl_access_ok(ua, Client_ACL, res->res_client.hdr.name)) { + if (!acl_access_ok(ua, Client_ACL, res->res_client.name())) { break; } - sendit(sock, _("Client: Name=%s Enabled=%d Address=%s FDport=%d MaxJobs=%u\n"), - res->res_client.hdr.name, res->res_client.enabled, - res->res_client.address, res->res_client.FDport, - res->res_client.MaxConcurrentJobs); + sendit(sock, _("Client: Name=%s Enabled=%d Address=%s FDport=%d MaxJobs=%u NumJobs=%u\n"), + res->res_client.name(), res->res_client.is_enabled(), + res->res_client.address(), res->res_client.FDport, + res->res_client.MaxConcurrentJobs, res->res_client.getNumConcurrentJobs()); sendit(sock, _(" JobRetention=%s FileRetention=%s AutoPrune=%d\n"), edit_utime(res->res_client.JobRetention, ed1, sizeof(ed1)), edit_utime(res->res_client.FileRetention, ed2, sizeof(ed2)), @@ -656,14 +909,17 @@ void dump_resource(int type, RES *ares, void sendit(void *sock, const char *fmt, dev->VolumeName, dev->MediaType); break; + case R_AUTOCHANGER: case R_STORAGE: if (!acl_access_ok(ua, Storage_ACL, res->res_store.hdr.name)) { break; } - sendit(sock, _("Storage: name=%s address=%s SDport=%d MaxJobs=%u\n" + sendit(sock, _("%s: name=%s address=%s SDport=%d MaxJobs=%u NumJobs=%u\n" " DeviceName=%s MediaType=%s StorageId=%s Autochanger=%d\n"), + res->res_store.changer == &res->res_store ? "Autochanger" : "Storage", res->res_store.hdr.name, res->res_store.address, res->res_store.SDport, res->res_store.MaxConcurrentJobs, + res->res_store.getNumConcurrentJobs(), res->res_store.dev_name(), res->res_store.media_type, edit_int64(res->res_store.StorageId, ed1), @@ -671,6 +927,15 @@ void dump_resource(int type, RES *ares, void sendit(void *sock, const char *fmt, if (res->res_store.fd_storage_address) { sendit(sock, " FDStorageAddress=%s\n", res->res_store.fd_storage_address); } + if (res->res_store.ac_group) { + STORE *shstore = res->res_store.shared_storage; + sendit(sock, " AC group=%s ShareStore=%s\n", res->res_store.ac_group, + shstore?shstore->name():"*none*"); + } + if (res->res_store.changer && res->res_store.changer != &res->res_store) { + sendit(sock, _(" Parent --> ")); + dump_resource(-R_STORAGE, (RES *)res->res_store.changer, sendit, sock); + } break; case R_CATALOG: @@ -694,9 +959,10 @@ void dump_resource(int type, RES *ares, void sendit(void *sock, const char *fmt, type == R_JOB ? _("Job") : _("JobDefs"), res->res_job.hdr.name, res->res_job.JobType, level_to_str(res->res_job.JobLevel), res->res_job.Priority, - res->res_job.enabled); - sendit(sock, _(" MaxJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"), + res->res_job.is_enabled()); + sendit(sock, _(" MaxJobs=%u NumJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"), res->res_job.MaxConcurrentJobs, + res->res_job.getNumConcurrentJobs(), res->res_job.RescheduleOnError, res->res_job.RescheduleTimes, edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1), res->res_job.spool_data, res->res_job.write_part_after_job); @@ -796,6 +1062,10 @@ void dump_resource(int type, RES *ares, void sendit(void *sock, const char *fmt, sendit(sock, _(" --> DifferentialBackup")); dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock); } + if (res->res_job.next_pool) { + sendit(sock, _(" --> Next")); /* Pool will be added by dump_resource */ + dump_resource(-R_POOL, (RES *)res->res_job.next_pool, sendit, sock); + } if (res->res_job.verify_job) { sendit(sock, _(" --> ")); dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock); @@ -821,7 +1091,7 @@ void dump_resource(int type, RES *ares, void sendit(void *sock, const char *fmt, if (!acl_access_ok(ua, FileSet_ACL, res->res_fs.hdr.name)) { break; } - sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name); + sendit(sock, _("FileSet: name=%s IgnoreFileSetChanges=%d\n"), res->res_fs.hdr.name, res->res_fs.ignore_fs_changes); for (i=0; ires_fs.num_includes; i++) { INCEXE *incexe = res->res_fs.include_items[i]; for (j=0; jnum_opts; j++) { @@ -916,7 +1186,7 @@ void dump_resource(int type, RES *ares, void sendit(void *sock, const char *fmt, RUN *run = res->res_sch.run; char buf[1000], num[30]; sendit(sock, _("Schedule: Name=%s Enabled=%d\n"), - res->res_sch.hdr.name, res->res_sch.enabled); + res->res_sch.hdr.name, res->res_sch.is_enabled()); if (!run) { break; } @@ -987,6 +1257,10 @@ next_run: sendit(sock, _(" --> ")); dump_resource(-R_POOL, (RES *)run->pool, sendit, sock); } + if (run->next_pool) { + sendit(sock, _(" --> Next")); /* Pool will be added by dump_resource */ + dump_resource(-R_POOL, (RES *)run->next_pool, sendit, sock); + } if (run->storage) { sendit(sock, _(" --> ")); dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock); @@ -1035,6 +1309,8 @@ next_run: edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)), edit_uint64(res->res_pool.MigrationHighBytes, ed2), edit_uint64(res->res_pool.MigrationLowBytes, ed3)); + sendit(sock, _(" CacheRetention=%s\n"), + edit_utime(res->res_pool.CacheRetention, ed1, sizeof(ed1))); sendit(sock, _(" JobRetention=%s FileRetention=%s\n"), edit_utime(res->res_pool.JobRetention, ed1, sizeof(ed1)), edit_utime(res->res_pool.FileRetention, ed2, sizeof(ed2))); @@ -1079,10 +1355,12 @@ next_run: sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type); break; } - if (recurse && res->res_dir.hdr.next) { - dump_resource(type, res->res_dir.hdr.next, sendit, sock); + if (recurse) { + next = GetNextRes(0, (RES *)res); + if (next) { + dump_resource(type, next, sendit, sock); + } } - } /* @@ -1135,7 +1413,6 @@ static void free_incexe(INCEXE *incexe) void free_resource(RES *rres, int type) { int num; - RES *nres; URES *res = (URES *)rres; if (res == NULL) { @@ -1144,7 +1421,6 @@ void free_resource(RES *rres, int type) Dmsg3(200, "type=%d res=%p name=%s\n", type, res, res->res_dir.hdr.name); /* common stuff -- free the resource name and description */ - nres = (RES *)res->res_dir.hdr.next; if (res->res_dir.hdr.name) { free(res->res_dir.hdr.name); } @@ -1242,8 +1518,8 @@ void free_resource(RES *rres, int type) } break; case R_CLIENT: - if (res->res_client.address) { - free(res->res_client.address); + if (res->res_client.client_address) { + free(res->res_client.client_address); } if (res->res_client.fd_storage_address) { free(res->res_client.fd_storage_address); @@ -1270,6 +1546,7 @@ void free_resource(RES *rres, int type) delete res->res_client.tls_allowed_cns; } break; + case R_AUTOCHANGER: case R_STORAGE: if (res->res_store.address) { free(res->res_store.address); @@ -1283,6 +1560,9 @@ void free_resource(RES *rres, int type) if (res->res_store.media_type) { free(res->res_store.media_type); } + if (res->res_store.ac_group) { + free_pool_memory(res->res_store.ac_group); + } if (res->res_store.device) { delete res->res_store.device; } @@ -1438,9 +1718,6 @@ void free_resource(RES *rres, int type) if (res) { free(res); } - if (nres) { - free_resource(nres, type); - } } /* @@ -1449,7 +1726,7 @@ void free_resource(RES *rres, int type) * pointers because they may not have been defined until * later in pass 1. */ -void save_resource(int type, RES_ITEM *items, int pass) +bool save_resource(CONFIG *config, int type, RES_ITEM *items, int pass) { URES *res; int rindex = type - r_first; @@ -1464,13 +1741,15 @@ void save_resource(int type, RES_ITEM *items, int pass) for (i=0; items[i].name; i++) { if (items[i].flags & ITEM_REQUIRED) { if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) { - Emsg2(M_ERROR_TERM, 0, _("\"%s\" directive is required in \"%s\" resource, but not found.\n"), + Mmsg(config->m_errmsg, _("\"%s\" directive is required in \"%s\" resource, but not found.\n"), items[i].name, resources[rindex].name); + return false; } } /* If this triggers, take a look at lib/parse_conf.h */ if (i >= MAX_RES_ITEMS) { - Emsg1(M_ERROR_TERM, 0, _("Too many directive in \"%s\" resource\n"), resources[rindex].name); + Mmsg(config->m_errmsg, _("Too many directives in \"%s\" resource\n"), resources[rindex].name); + return false; } } } else if (type == R_JOB) { @@ -1479,8 +1758,9 @@ void save_resource(int type, RES_ITEM *items, int pass) */ if (items[0].flags & ITEM_REQUIRED) { if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) { - Emsg2(M_ERROR_TERM, 0, _("\"%s\" directive is required in \"%s\" resource, but not found.\n"), - items[0].name, resources[rindex].name); + Mmsg(config->m_errmsg, _("\"%s\" directive is required in \"%s\" resource, but not found.\n"), + items[0].name, resources[rindex].name); + return false; } } } @@ -1510,7 +1790,8 @@ void save_resource(int type, RES_ITEM *items, int pass) case R_POOL: /* Find resource saved in pass 1 */ if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) { - Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name); + Mmsg(config->m_errmsg, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name); + return false; } /* Explicitly copy resource pointers from this pass (res_all) */ res->res_pool.NextPool = res_all.res_pool.NextPool; @@ -1521,30 +1802,43 @@ void save_resource(int type, RES_ITEM *items, int pass) break; case R_CONSOLE: if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) { - Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name); + Mmsg(config->m_errmsg, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name); + return false; } res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns; break; case R_DIRECTOR: if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) { - Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name); + Mmsg(config->m_errmsg, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name); + return false; } res->res_dir.messages = res_all.res_dir.messages; res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns; break; + case R_AUTOCHANGER: /* alias for R_STORAGE */ case R_STORAGE: + type = R_STORAGE; /* force Storage type */ if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) { - Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"), - res_all.res_dir.hdr.name); + Mmsg(config->m_errmsg, _("Cannot find Storage resource %s\n"), + res_all.res_dir.hdr.name); + return false; } /* we must explicitly copy the device alist pointer */ res->res_store.device = res_all.res_store.device; + res->res_store.changer = res_all.res_store.changer; + res->res_store.shared_storage = res_all.res_store.shared_storage; + res->res_store.autochanger = res_all.res_store.autochanger; + if (strcasecmp(resources[rindex].name, "autochanger") == 0) { + res->res_store.changer = &res->res_store; + res->res_store.autochanger = true; + } break; case R_JOB: case R_JOBDEFS: if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) { - Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"), - res_all.res_dir.hdr.name); + Mmsg(config->m_errmsg, _("Cannot find Job resource %s\n"), + res_all.res_dir.hdr.name); + return false; } res->res_job.messages = res_all.res_job.messages; res->res_job.schedule = res_all.res_job.schedule; @@ -1594,15 +1888,17 @@ void save_resource(int type, RES_ITEM *items, int pass) break; case R_COUNTER: if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) { - Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name); + Mmsg(config->m_errmsg, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name); + return false; } res->res_counter.Catalog = res_all.res_counter.Catalog; res->res_counter.WrapCounter = res_all.res_counter.WrapCounter; break; case R_CLIENT: - if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) { - Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name); + if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.name())) == NULL) { + Mmsg(config->m_errmsg, _("Cannot find Client resource %s\n"), res_all.res_client.name()); + return false; } res->res_client.catalog = res_all.res_client.catalog; res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns; @@ -1614,8 +1910,9 @@ void save_resource(int type, RES_ITEM *items, int pass) * in by run_conf.c during pass 2, so here we jam the pointer * into the Schedule resource. */ - if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) { - Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name); + if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.name())) == NULL) { + Mmsg(config->m_errmsg, _("Cannot find Schedule resource %s\n"), res_all.res_client.name()); + return false; } res->res_sch.run = res_all.res_sch.run; break; @@ -1635,7 +1932,13 @@ void save_resource(int type, RES_ITEM *items, int pass) free(res_all.res_dir.hdr.desc); res_all.res_dir.hdr.desc = NULL; } - return; + return true; + } + + /* R_AUTOCHANGER is alias so turn it into an R_STORAGE */ + if (type == R_AUTOCHANGER) { + type = R_STORAGE; + rindex = type - r_first; } /* @@ -1686,32 +1989,11 @@ void save_resource(int type, RES_ITEM *items, int pass) } /* Common */ if (!error) { - res = (URES *)malloc(size); - memcpy(res, &res_all, size); - if (!res_head[rindex]) { - res_head[rindex] = (RES *)res; /* store first entry */ - Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type), - res->res_dir.hdr.name, rindex); - } else { - RES *next, *last; - if (res->res_dir.hdr.name == NULL) { - Emsg1(M_ERROR_TERM, 0, _("A Name directive is required in \"%s\" resource, but not found.\n"), - resources[rindex].name); - } - /* Add new res to end of chain */ - for (last=next=res_head[rindex]; next; next=next->next) { - last = next; - if (strcmp(next->name, res->res_dir.hdr.name) == 0) { - Emsg2(M_ERROR_TERM, 0, - _("Attempt to define second \"%s\" resource named \"%s\" is not permitted.\n"), - resources[rindex].name, res->res_dir.hdr.name); - } - } - last->next = (RES *)res; - Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type), - res->res_dir.hdr.name, rindex, pass); + if (!config->insert_res(rindex, size)) { + return false; } } + return true; } void store_actiononpurge(LEX *lc, RES_ITEM *item, int index, int pass) @@ -1728,46 +2010,93 @@ void store_actiononpurge(LEX *lc, RES_ITEM *item, int index, int pass) set_bit(index, res_all.hdr.item_present); } +/* + * Store an autochanger resource. Used by Autochanger and + * SharedStorage direcives. + */ +void store_ac_res(LEX *lc, RES_ITEM *item, int index, int pass) +{ + RES *res; + RES_ITEM *next = item + 1; + + lex_get_token(lc, T_NAME); + Dmsg1(100, "Got name=%s\n", lc->str); + /* + * For backward compatibility, if yes/no, set the next item + */ + if (strcasecmp(item->name, "autochanger") == 0) { + if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) { + *(bool *)(next->value) = true; + Dmsg2(100, "Item=%s got value=%s\n", item->name, lc->str); + scan_to_eol(lc); + return; + } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) { + *(bool *)(next->value) = false; + Dmsg2(100, "Item=%s got value=%s\n", item->name, lc->str); + scan_to_eol(lc); + return; + } + } + Dmsg2(100, "Item=%s got value=%s\n", item->name, lc->str); + + if (pass == 2) { + res = GetResWithName(R_STORAGE, lc->str); + if (res == NULL) { + scan_err3(lc, _("Could not find Storage Resource %s referenced on line %d : %s\n"), + lc->str, lc->line_no, lc->line); + return; + } + if (*(item->value)) { + scan_err3(lc, _("Attempt to redefine Storage resource \"%s\" referenced on line %d : %s\n"), + item->name, lc->line_no, lc->line); + return; + } + Dmsg2(100, "Store %s value=%p\n", lc->str, res); + *(item->value) = (char *)res; + *(bool *)(next->value) = true; + } + scan_to_eol(lc); + set_bit(index, res_all.hdr.item_present); +} + + /* * Store Device. Note, the resource is created upon the * first reference. The details of the resource are obtained * later from the SD. */ -static void store_device(LEX *lc, RES_ITEM *item, int index, int pass) +void store_device(LEX *lc, RES_ITEM *item, int index, int pass) { - URES *res; int rindex = R_DEVICE - r_first; int size = sizeof(DEVICE); - bool found = false; if (pass == 1) { + URES *ures; + RES *res; + lex_get_token(lc, T_NAME); - if (!res_head[rindex]) { - res = (URES *)malloc(size); - memset(res, 0, size); - res->res_dev.hdr.name = bstrdup(lc->str); - res_head[rindex] = (RES *)res; /* store first entry */ - Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE), - res->res_dir.hdr.name, rindex); + rblist *list = res_head[rindex]->res_list; + ures = (URES *)malloc(size); + memset(ures, 0, size); + ures->res_dev.hdr.name = bstrdup(lc->str); + res = (RES *)ures; + if (list->empty()) { + list->insert(res, res_compare); + res_head[rindex]->first = res; + res_head[rindex]->last = res; } else { - RES *next; - /* See if it is already defined */ - for (next=res_head[rindex]; next->next; next=next->next) { - if (strcmp(next->name, lc->str) == 0) { - found = true; - break; - } - } - if (!found) { - res = (URES *)malloc(size); - memset(res, 0, size); - res->res_dev.hdr.name = bstrdup(lc->str); - next->next = (RES *)res; - Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE), - res->res_dir.hdr.name, rindex, pass); + RES *item, *prev; + prev = res_head[rindex]->last; + item = (RES *)list->insert(res, res_compare); + if (item == res) { + prev->res_next = res; + res_head[rindex]->last = res; + } else { + /* res not inserted */ + free(ures->res_dev.hdr.name); + free(ures); } } - scan_to_eol(lc); set_bit(index, res_all.hdr.item_present); } else { @@ -2159,7 +2488,7 @@ void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass) /* callback function for edit_job_codes */ /* See ../lib/util.c, function edit_job_codes, for more remaining codes */ -extern "C" char *job_code_callback_director(JCR *jcr, const char* param) +extern "C" char *job_code_callback_director(JCR *jcr, const char* param, char *buf, int buflen) { static char yes[] = "yes"; static char no[] = "no"; @@ -2175,8 +2504,8 @@ extern "C" char *job_code_callback_director(JCR *jcr, const char* param) } break; case 'h': - if (jcr->client && jcr->client->address) { - return jcr->client->address; + if (jcr->client && jcr->client->address()) { + return jcr->client->address(); } break; case 'p': @@ -2195,6 +2524,16 @@ extern "C" char *job_code_callback_director(JCR *jcr, const char* param) return my_name; case 'C': return jcr->cloned ? yes : no; + case 'I': + if (buflen >= 50) { + if (jcr->wjcr) { + edit_uint64(jcr->wjcr->JobId, buf); + return buf; + } else { + edit_uint64(0, buf); + return buf; + } + } } return nothing; } @@ -2202,6 +2541,6 @@ extern "C" char *job_code_callback_director(JCR *jcr, const char* param) bool parse_dir_config(CONFIG *config, const char *configfile, int exit_code) { config->init(configfile, NULL, exit_code, (void *)&res_all, res_all_size, - r_first, r_last, resources, res_head); + r_first, r_last, resources, &res_head); return config->parse_config(); }