From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 917A01FF18A for ; Mon, 26 May 2025 16:15:23 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id A4BC1340DE; Mon, 26 May 2025 16:15:36 +0200 (CEST) From: Hannes Laimer To: pbs-devel@lists.proxmox.com Date: Mon, 26 May 2025 16:14:45 +0200 Message-Id: <20250526141445.228717-13-h.laimer@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250526141445.228717-1-h.laimer@proxmox.com> References: <20250526141445.228717-1-h.laimer@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.026 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pbs-devel] [PATCH proxmox-backup v2 12/12] api/server: replace datastore_lookup with new, state-typed datastore returning functions X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox Backup Server development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pbs-devel-bounces@lists.proxmox.com Sender: "pbs-devel" Signed-off-by: Hannes Laimer --- pbs-datastore/src/snapshot_reader.rs | 6 ++---- src/api2/admin/datastore.rs | 18 +++++++++--------- src/api2/admin/namespace.rs | 10 +++++----- src/api2/backup/mod.rs | 8 ++++---- src/api2/reader/mod.rs | 8 ++++---- src/api2/status/mod.rs | 8 ++++---- src/api2/tape/backup.rs | 10 +++++----- src/api2/tape/restore.rs | 12 ++++++------ src/bin/proxmox-backup-proxy.rs | 4 ++-- src/server/prune_job.rs | 4 ++-- src/server/pull.rs | 9 ++++----- src/server/push.rs | 4 ++-- src/server/verify_job.rs | 4 ++-- 13 files changed, 51 insertions(+), 54 deletions(-) diff --git a/pbs-datastore/src/snapshot_reader.rs b/pbs-datastore/src/snapshot_reader.rs index d604507d..6ece122f 100644 --- a/pbs-datastore/src/snapshot_reader.rs +++ b/pbs-datastore/src/snapshot_reader.rs @@ -11,8 +11,7 @@ use nix::sys::stat::Mode; use pbs_config::BackupLockGuard; use pbs_api_types::{ - print_store_and_ns, ArchiveType, BackupNamespace, Operation, CLIENT_LOG_BLOB_NAME, - MANIFEST_BLOB_NAME, + print_store_and_ns, ArchiveType, BackupNamespace, CLIENT_LOG_BLOB_NAME, MANIFEST_BLOB_NAME, }; use crate::backup_info::BackupDir; @@ -163,9 +162,8 @@ impl bool, T: CanRead> Iterator for SnapshotChunkIterator<'_ ), }; - let datastore = DataStore::lookup_datastore( + let datastore = DataStore::lookup_datastore_read( self.snapshot_reader.datastore_name(), - Some(Operation::Read), )?; let order = datastore.get_chunks_in_order(&*index, &self.skip_fn, |_| Ok(()))?; diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs index 218d7e73..dfcb9123 100644 --- a/src/api2/admin/datastore.rs +++ b/src/api2/admin/datastore.rs @@ -203,7 +203,7 @@ pub fn list_groups( PRIV_DATASTORE_BACKUP, )?; - let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?; + let datastore = DataStore::lookup_datastore_read(&store)?; datastore .iter_backup_groups(ns.clone())? // FIXME: Namespaces and recursion parameters! @@ -508,7 +508,7 @@ unsafe fn list_snapshots_blocking( PRIV_DATASTORE_BACKUP, )?; - let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?; + let datastore = DataStore::lookup_datastore_read(&store)?; // FIXME: filter also owner before collecting, for doing that nicely the owner should move into // backup group and provide an error free (Err -> None) accessor @@ -720,7 +720,7 @@ pub async fn status( } }; - let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?; + let datastore = DataStore::lookup_datastore_read(&store)?; let (counts, gc_status) = if verbose { let filter_owner = if store_privs & PRIV_DATASTORE_AUDIT != 0 { @@ -833,7 +833,7 @@ pub fn verify( PRIV_DATASTORE_BACKUP, )?; - let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?; + let datastore = DataStore::lookup_datastore_write(&store)?; let ignore_verified = ignore_verified.unwrap_or(true); let worker_id; @@ -1182,7 +1182,7 @@ pub fn prune_datastore( true, )?; - let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?; + let datastore = DataStore::lookup_datastore_write(&store)?; let ns = prune_options.ns.clone().unwrap_or_default(); let worker_id = format!("{}:{}", store, ns); @@ -1220,7 +1220,7 @@ pub fn start_garbage_collection( _info: &ApiMethod, rpcenv: &mut dyn RpcEnvironment, ) -> Result { - let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?; + let datastore = DataStore::lookup_datastore_write(&store)?; let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let job = Job::new("garbage_collection", &store) @@ -1267,7 +1267,7 @@ pub fn garbage_collection_status( ..Default::default() }; - let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?; + let datastore = DataStore::lookup_datastore_read(&store)?; let status_in_memory = datastore.last_gc_status(); let state_file = JobState::load("garbage_collection", &store) .map_err(|err| log::error!("could not open GC statefile for {store}: {err}")) @@ -1973,7 +1973,7 @@ pub fn get_rrd_stats( cf: RrdMode, _param: Value, ) -> Result { - let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?; + let datastore = DataStore::lookup_datastore_read(&store)?; let disk_manager = crate::tools::disks::DiskManage::new(); let mut rrd_fields = vec![ @@ -2353,7 +2353,7 @@ pub async fn set_backup_owner( PRIV_DATASTORE_BACKUP, )?; - let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?; + let datastore = DataStore::lookup_datastore_write(&store)?; let backup_group = datastore.backup_group(ns, backup_group); let owner = backup_group.get_owner()?; diff --git a/src/api2/admin/namespace.rs b/src/api2/admin/namespace.rs index 6cf88d89..44a31269 100644 --- a/src/api2/admin/namespace.rs +++ b/src/api2/admin/namespace.rs @@ -5,8 +5,8 @@ use proxmox_router::{http_bail, ApiMethod, Permission, Router, RpcEnvironment}; use proxmox_schema::*; use pbs_api_types::{ - Authid, BackupGroupDeleteStats, BackupNamespace, NamespaceListItem, Operation, - DATASTORE_SCHEMA, NS_MAX_DEPTH_SCHEMA, PROXMOX_SAFE_ID_FORMAT, + Authid, BackupGroupDeleteStats, BackupNamespace, NamespaceListItem, DATASTORE_SCHEMA, + NS_MAX_DEPTH_SCHEMA, PROXMOX_SAFE_ID_FORMAT, }; use pbs_datastore::DataStore; @@ -54,7 +54,7 @@ pub fn create_namespace( check_ns_modification_privs(&store, &ns, &auth_id)?; - let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?; + let datastore = DataStore::lookup_datastore_write(&store)?; datastore.create_namespace(&parent, name) } @@ -97,7 +97,7 @@ pub fn list_namespaces( // get result up-front to avoid cloning NS, it's relatively cheap anyway (no IO normally) let parent_access = check_ns_privs(&store, &parent, &auth_id, NS_PRIVS_OK); - let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?; + let datastore = DataStore::lookup_datastore_read(&store)?; let iter = match datastore.recursive_iter_backup_ns_ok(parent, max_depth) { Ok(iter) => iter, @@ -162,7 +162,7 @@ pub fn delete_namespace( check_ns_modification_privs(&store, &ns, &auth_id)?; - let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?; + let datastore = DataStore::lookup_datastore_write(&store)?; let (removed_all, stats) = datastore.remove_namespace_recursive(&ns, delete_groups)?; if !removed_all { diff --git a/src/api2/backup/mod.rs b/src/api2/backup/mod.rs index 79354dbf..c160080e 100644 --- a/src/api2/backup/mod.rs +++ b/src/api2/backup/mod.rs @@ -19,9 +19,9 @@ use proxmox_schema::*; use proxmox_sortable_macro::sortable; use pbs_api_types::{ - ArchiveType, Authid, BackupNamespace, BackupType, Operation, VerifyState, - BACKUP_ARCHIVE_NAME_SCHEMA, BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA, BACKUP_TIME_SCHEMA, - BACKUP_TYPE_SCHEMA, CHUNK_DIGEST_SCHEMA, DATASTORE_SCHEMA, PRIV_DATASTORE_BACKUP, + ArchiveType, Authid, BackupNamespace, BackupType, VerifyState, BACKUP_ARCHIVE_NAME_SCHEMA, + BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA, BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA, + CHUNK_DIGEST_SCHEMA, DATASTORE_SCHEMA, PRIV_DATASTORE_BACKUP, }; use pbs_config::CachedUserInfo; use pbs_datastore::chunk_store::{Read as R, Write as W}; @@ -96,7 +96,7 @@ fn upgrade_to_backup_protocol( ) .map_err(|err| http_err!(FORBIDDEN, "{err}"))?; - let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?; + let datastore = DataStore::lookup_datastore_write(&store)?; let protocols = parts .headers diff --git a/src/api2/reader/mod.rs b/src/api2/reader/mod.rs index 52f0953a..ec9fb751 100644 --- a/src/api2/reader/mod.rs +++ b/src/api2/reader/mod.rs @@ -18,9 +18,9 @@ use proxmox_schema::{BooleanSchema, ObjectSchema}; use proxmox_sortable_macro::sortable; use pbs_api_types::{ - ArchiveType, Authid, Operation, BACKUP_ARCHIVE_NAME_SCHEMA, BACKUP_ID_SCHEMA, - BACKUP_NAMESPACE_SCHEMA, BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA, CHUNK_DIGEST_SCHEMA, - DATASTORE_SCHEMA, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_READ, + ArchiveType, Authid, BACKUP_ARCHIVE_NAME_SCHEMA, BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA, + BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA, CHUNK_DIGEST_SCHEMA, DATASTORE_SCHEMA, + PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_READ, }; use pbs_config::CachedUserInfo; use pbs_datastore::chunk_store::Read as R; @@ -92,7 +92,7 @@ fn upgrade_to_backup_reader_protocol( bail!("no permissions on /{}", acl_path.join("/")); } - let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?; + let datastore = DataStore::lookup_datastore_read(&store)?; let backup_dir = pbs_api_types::BackupDir::deserialize(¶m)?; diff --git a/src/api2/status/mod.rs b/src/api2/status/mod.rs index e066a99c..5a85cf80 100644 --- a/src/api2/status/mod.rs +++ b/src/api2/status/mod.rs @@ -10,8 +10,8 @@ use proxmox_schema::api; use proxmox_sortable_macro::sortable; use pbs_api_types::{ - Authid, DataStoreConfig, DataStoreMountStatus, DataStoreStatusListItem, Operation, - PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, + Authid, DataStoreConfig, DataStoreMountStatus, DataStoreStatusListItem, PRIV_DATASTORE_AUDIT, + PRIV_DATASTORE_BACKUP, }; use pbs_config::CachedUserInfo; @@ -69,7 +69,7 @@ pub async fn datastore_status( }; if !allowed { - if let Ok(datastore) = DataStore::lookup_datastore(store, Some(Operation::Lookup)) { + if let Ok(datastore) = DataStore::lookup_datastore_read(store) { if can_access_any_namespace(datastore, &auth_id, &user_info) { list.push(DataStoreStatusListItem::empty(store, None, mount_status)); } @@ -77,7 +77,7 @@ pub async fn datastore_status( continue; } - let datastore = match DataStore::lookup_datastore(store, Some(Operation::Read)) { + let datastore = match DataStore::lookup_datastore_read(store) { Ok(datastore) => datastore, Err(err) => { list.push(DataStoreStatusListItem::empty( diff --git a/src/api2/tape/backup.rs b/src/api2/tape/backup.rs index 306d5936..d9ff7ba9 100644 --- a/src/api2/tape/backup.rs +++ b/src/api2/tape/backup.rs @@ -11,9 +11,9 @@ use proxmox_schema::api; use proxmox_worker_task::WorkerTaskContext; use pbs_api_types::{ - print_ns_and_snapshot, print_store_and_ns, Authid, MediaPoolConfig, Operation, - TapeBackupJobConfig, TapeBackupJobSetup, TapeBackupJobStatus, JOB_ID_SCHEMA, - PRIV_DATASTORE_READ, PRIV_TAPE_AUDIT, PRIV_TAPE_WRITE, UPID_SCHEMA, + print_ns_and_snapshot, print_store_and_ns, Authid, MediaPoolConfig, TapeBackupJobConfig, + TapeBackupJobSetup, TapeBackupJobStatus, JOB_ID_SCHEMA, PRIV_DATASTORE_READ, PRIV_TAPE_AUDIT, + PRIV_TAPE_WRITE, UPID_SCHEMA, }; use pbs_config::CachedUserInfo; @@ -151,7 +151,7 @@ pub fn do_tape_backup_job( let worker_type = job.jobtype().to_string(); - let datastore = DataStore::lookup_datastore(&setup.store, Some(Operation::Read))?; + let datastore = DataStore::lookup_datastore_read(&setup.store)?; let (config, _digest) = pbs_config::media_pool::config()?; let pool_config: MediaPoolConfig = config.lookup("pool", &setup.pool)?; @@ -307,7 +307,7 @@ pub fn backup( check_backup_permission(&auth_id, &setup.store, &setup.pool, &setup.drive)?; - let datastore = DataStore::lookup_datastore(&setup.store, Some(Operation::Read))?; + let datastore = DataStore::lookup_datastore_read(&setup.store)?; let (config, _digest) = pbs_config::media_pool::config()?; let pool_config: MediaPoolConfig = config.lookup("pool", &setup.pool)?; diff --git a/src/api2/tape/restore.rs b/src/api2/tape/restore.rs index 8f089c20..1147623b 100644 --- a/src/api2/tape/restore.rs +++ b/src/api2/tape/restore.rs @@ -20,10 +20,10 @@ use proxmox_worker_task::WorkerTaskContext; use pbs_api_types::{ parse_ns_and_snapshot, print_ns_and_snapshot, ArchiveType, Authid, BackupDir, BackupNamespace, - CryptMode, NotificationMode, Operation, TapeRestoreNamespace, Userid, - DATASTORE_MAP_ARRAY_SCHEMA, DATASTORE_MAP_LIST_SCHEMA, DRIVE_NAME_SCHEMA, MANIFEST_BLOB_NAME, - MAX_NAMESPACE_DEPTH, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_MODIFY, PRIV_TAPE_READ, - TAPE_RESTORE_NAMESPACE_SCHEMA, TAPE_RESTORE_SNAPSHOT_SCHEMA, UPID_SCHEMA, + CryptMode, NotificationMode, TapeRestoreNamespace, Userid, DATASTORE_MAP_ARRAY_SCHEMA, + DATASTORE_MAP_LIST_SCHEMA, DRIVE_NAME_SCHEMA, MANIFEST_BLOB_NAME, MAX_NAMESPACE_DEPTH, + PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_MODIFY, PRIV_TAPE_READ, TAPE_RESTORE_NAMESPACE_SCHEMA, + TAPE_RESTORE_SNAPSHOT_SCHEMA, UPID_SCHEMA, }; use pbs_client::pxar::tools::handle_root_with_optional_format_version_prelude; use pbs_config::CachedUserInfo; @@ -145,10 +145,10 @@ impl TryFrom for DataStoreMap { if let Some(index) = store.find('=') { let mut target = store.split_off(index); target.remove(0); // remove '=' - let datastore = DataStore::lookup_datastore(&target, Some(Operation::Write))?; + let datastore = DataStore::lookup_datastore_write(&target)?; map.insert(store, datastore); } else if default.is_none() { - default = Some(DataStore::lookup_datastore(&store, Some(Operation::Write))?); + default = Some(DataStore::lookup_datastore_write(&store)?); } else { bail!("multiple default stores given"); } diff --git a/src/bin/proxmox-backup-proxy.rs b/src/bin/proxmox-backup-proxy.rs index 643a2dbd..4f5d9681 100644 --- a/src/bin/proxmox-backup-proxy.rs +++ b/src/bin/proxmox-backup-proxy.rs @@ -40,7 +40,7 @@ use pbs_buildcfg::configdir; use proxmox_time::CalendarEvent; use pbs_api_types::{ - Authid, DataStoreConfig, Operation, PruneJobConfig, SyncJobConfig, TapeBackupJobConfig, + Authid, DataStoreConfig, PruneJobConfig, SyncJobConfig, TapeBackupJobConfig, VerificationJobConfig, }; @@ -516,7 +516,7 @@ async fn schedule_datastore_garbage_collection() { Err(_) => continue, // could not get lock }; - let datastore = match DataStore::lookup_datastore(&store, Some(Operation::Write)) { + let datastore = match DataStore::lookup_datastore_write(&store) { Ok(datastore) => datastore, Err(err) => { log::warn!("skipping scheduled GC on {store}, could look it up - {err}"); diff --git a/src/server/prune_job.rs b/src/server/prune_job.rs index 395aaee4..20cb9218 100644 --- a/src/server/prune_job.rs +++ b/src/server/prune_job.rs @@ -4,7 +4,7 @@ use anyhow::Error; use tracing::{info, warn}; use pbs_api_types::{ - print_store_and_ns, Authid, KeepOptions, Operation, PruneJobOptions, MAX_NAMESPACE_DEPTH, + print_store_and_ns, Authid, KeepOptions, PruneJobOptions, MAX_NAMESPACE_DEPTH, PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_PRUNE, }; use pbs_datastore::chunk_store::CanWrite; @@ -128,7 +128,7 @@ pub fn do_prune_job( auth_id: &Authid, schedule: Option, ) -> Result { - let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?; + let datastore = DataStore::lookup_datastore_write(&store)?; let worker_type = job.jobtype().to_string(); let auth_id = auth_id.clone(); diff --git a/src/server/pull.rs b/src/server/pull.rs index 573aa805..d885a3a8 100644 --- a/src/server/pull.rs +++ b/src/server/pull.rs @@ -12,9 +12,8 @@ use tracing::info; use pbs_api_types::{ print_store_and_ns, ArchiveType, Authid, BackupArchiveName, BackupDir, BackupGroup, - BackupNamespace, GroupFilter, Operation, RateLimitConfig, Remote, VerifyState, - CLIENT_LOG_BLOB_NAME, MANIFEST_BLOB_NAME, MAX_NAMESPACE_DEPTH, PRIV_DATASTORE_AUDIT, - PRIV_DATASTORE_BACKUP, + BackupNamespace, GroupFilter, RateLimitConfig, Remote, VerifyState, CLIENT_LOG_BLOB_NAME, + MANIFEST_BLOB_NAME, MAX_NAMESPACE_DEPTH, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, }; use pbs_client::BackupRepository; use pbs_config::CachedUserInfo; @@ -110,12 +109,12 @@ impl PullParameters { }) } else { Arc::new(LocalSource { - store: DataStore::lookup_datastore(remote_store, Some(Operation::Read))?, + store: DataStore::lookup_datastore_read(remote_store)?, ns: remote_ns, }) }; let target = PullTarget { - store: DataStore::lookup_datastore(store, Some(Operation::Write))?, + store: DataStore::lookup_datastore_write(store)?, ns, }; diff --git a/src/server/push.rs b/src/server/push.rs index ff9d9358..532fc688 100644 --- a/src/server/push.rs +++ b/src/server/push.rs @@ -12,7 +12,7 @@ use tracing::{info, warn}; use pbs_api_types::{ print_store_and_ns, ApiVersion, ApiVersionInfo, ArchiveType, Authid, BackupArchiveName, BackupDir, BackupGroup, BackupGroupDeleteStats, BackupNamespace, GroupFilter, GroupListItem, - NamespaceListItem, Operation, RateLimitConfig, Remote, SnapshotListItem, CLIENT_LOG_BLOB_NAME, + NamespaceListItem, RateLimitConfig, Remote, SnapshotListItem, CLIENT_LOG_BLOB_NAME, MANIFEST_BLOB_NAME, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_READ, PRIV_REMOTE_DATASTORE_BACKUP, PRIV_REMOTE_DATASTORE_MODIFY, PRIV_REMOTE_DATASTORE_PRUNE, }; @@ -107,7 +107,7 @@ impl PushParameters { let remove_vanished = remove_vanished.unwrap_or(false); let encrypted_only = encrypted_only.unwrap_or(false); let verified_only = verified_only.unwrap_or(false); - let store = DataStore::lookup_datastore(store, Some(Operation::Read))?; + let store = DataStore::lookup_datastore_read(store)?; if !store.namespace_exists(&ns) { bail!( diff --git a/src/server/verify_job.rs b/src/server/verify_job.rs index a15a257d..bd5253ed 100644 --- a/src/server/verify_job.rs +++ b/src/server/verify_job.rs @@ -1,7 +1,7 @@ use anyhow::{format_err, Error}; use tracing::{error, info}; -use pbs_api_types::{Authid, Operation, VerificationJobConfig}; +use pbs_api_types::{Authid, VerificationJobConfig}; use pbs_datastore::DataStore; use proxmox_rest_server::WorkerTask; @@ -18,7 +18,7 @@ pub fn do_verification_job( schedule: Option, to_stdout: bool, ) -> Result { - let datastore = DataStore::lookup_datastore(&verification_job.store, Some(Operation::Read))?; + let datastore = DataStore::lookup_datastore_write(&verification_job.store)?; let outdated_after = verification_job.outdated_after; let ignore_verified_snapshots = verification_job.ignore_verified.unwrap_or(true); -- 2.39.5 _______________________________________________ pbs-devel mailing list pbs-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel