diff --git a/doc/gvmd.8 b/doc/gvmd.8
index 7179be5d4..1460e997e 100644
--- a/doc/gvmd.8
+++ b/doc/gvmd.8
@@ -109,6 +109,9 @@ File mode of the unix socket
\fB--listen-owner=\fISTRING\fB\f1
Owner of the unix socket
.TP
+\fB--max-concurrent-scan-updates=\fINUMBER\fB\f1
+Maximum number of scan updates that can run at the same time. Default: 0 (unlimited).
+.TP
\fB--max-email-attachment-size=\fINUMBER\fB\f1
Maximum size of alert email attachments, in bytes.
.TP
diff --git a/doc/gvmd.8.xml b/doc/gvmd.8.xml
index 8e4bca59a..49bed2b70 100644
--- a/doc/gvmd.8.xml
+++ b/doc/gvmd.8.xml
@@ -262,6 +262,15 @@ along with this program. If not, see .
Owner of the unix socket
+
+ --max-concurrent-scan-updates=NUMBER
+
+
+ Maximum number of scan updates that can run at the same time.
+ Default: 0 (unlimited).
+
+
+
--max-email-attachment-size=NUMBER
diff --git a/doc/gvmd.html b/doc/gvmd.html
index 60ba46fe4..2a6b41dbd 100644
--- a/doc/gvmd.html
+++ b/doc/gvmd.html
@@ -217,6 +217,15 @@ Options
+ --max-concurrent-scan-updates=NUMBER
+
+
+ Maximum number of scan updates that can run at the same time.
+ Default: 0 (unlimited).
+
+
+
+
--max-email-attachment-size=NUMBER
Maximum size of alert email attachments, in bytes.
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2f27baf66..145e700f4 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -109,6 +109,7 @@ add_executable (manage-utils-test
debug_utils.c
gvmd.c gmpd.c
+ ipc.c
manage.c sql.c
manage_acl.c manage_configs.c manage_get.c
manage_license.c
@@ -140,6 +141,7 @@ add_executable (manage-test
debug_utils.c
gvmd.c gmpd.c
+ ipc.c
manage_utils.c sql.c
manage_acl.c manage_configs.c manage_get.c
manage_license.c
@@ -171,6 +173,7 @@ add_executable (manage-sql-test
debug_utils.c
gvmd.c gmpd.c
+ ipc.c
manage_utils.c manage.c sql.c
manage_acl.c manage_configs.c manage_get.c
manage_license.c
@@ -202,6 +205,7 @@ add_executable (gmp-tickets-test
debug_utils.c
gvmd.c gmpd.c
+ ipc.c
manage_utils.c manage.c sql.c
manage_acl.c manage_configs.c manage_get.c
manage_license.c
@@ -233,6 +237,7 @@ add_executable (utils-test
debug_utils.c
gvmd.c gmpd.c
+ ipc.c
manage_utils.c manage.c sql.c
manage_acl.c manage_configs.c manage_get.c
manage_license.c
@@ -281,6 +286,7 @@ add_executable (gvmd
main.c gvmd.c
debug_utils.c
gmpd.c
+ ipc.c
manage_utils.c manage.c sql.c
manage_acl.c manage_configs.c manage_get.c
manage_license.c
diff --git a/src/gvmd.c b/src/gvmd.c
index 9be3ecc8e..0fbe8051b 100644
--- a/src/gvmd.c
+++ b/src/gvmd.c
@@ -102,6 +102,7 @@
#include
#include "debug_utils.h"
+#include "ipc.h"
#include "manage.h"
#include "manage_sql_nvts.h"
#include "manage_sql_secinfo.h"
@@ -1895,6 +1896,7 @@ gvmd (int argc, char** argv, char *env[])
static gchar *broker_address = NULL;
static gchar *feed_lock_path = NULL;
static int feed_lock_timeout = 0;
+ static int max_concurrent_scan_updates = 0;
static int mem_wait_retries = 30;
static int min_mem_feed_update = 0;
static int vt_ref_insert_size = VT_REF_INSERT_SIZE_DEFAULT;
@@ -2073,6 +2075,11 @@ gvmd (int argc, char** argv, char *env[])
&listen_owner,
"Owner of the unix socket",
"" },
+ { "max-concurrent-scan-updates", '\0', 0, G_OPTION_ARG_INT,
+ &max_concurrent_scan_updates,
+ "Maximum number of scan updates that can run at the same time."
+ " Default: 0 (unlimited).",
+ "" },
{ "max-email-attachment-size", '\0', 0, G_OPTION_ARG_INT,
&max_email_attachment_size,
"Maximum size of alert email attachments, in bytes.",
@@ -2452,6 +2459,12 @@ gvmd (int argc, char** argv, char *env[])
g_debug ("Sentry support disabled");
}
+ /* Set maximum number of concurrent scan updates */
+ set_max_concurrent_scan_updates (max_concurrent_scan_updates);
+
+ /* Initialize Inter-Process Communication */
+ init_semaphore_set ();
+
/* Enable GNUTLS debugging if requested via env variable. */
{
const char *s;
diff --git a/src/ipc.c b/src/ipc.c
new file mode 100644
index 000000000..d22cca937
--- /dev/null
+++ b/src/ipc.c
@@ -0,0 +1,160 @@
+/* Copyright (C) 2024 Greenbone AG
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * @file ipc.c
+ * @brief Inter-process communitcation (IPC)
+ */
+
+/**
+ * @brief Enable extra GNU functions.
+ *
+ * semtimedop needs this
+ */
+#define _GNU_SOURCE
+
+#include
+#include
+
+#include "ipc.h"
+#include "manage.h"
+
+
+/**
+ * @brief System V semaphore set key for gvmd actions.
+ */
+static key_t semaphore_set_key = -1;
+
+/**
+ * @brief System V semaphore set id for gvmd actions.
+ */
+static int semaphore_set = -1;
+
+/**
+ * @brief Union type for values of semctl actions
+ */
+union semun {
+ int val; ///< Value for SETVAL
+ struct semid_ds *buf; ///< Buffer for IPC_STAT, IPC_SET
+ unsigned short *array; ///< Array for GETALL, SETALL
+ struct seminfo *__buf; ///< Buffer for IPC_INFO (Linux-specific)
+};
+
+/**
+ * @brief Initializes the semaphore set for gvmd actions.
+ *
+ * Needs max_concurrent_scan_updates to be set.
+ *
+ * @return 0 success, -1 error
+ */
+int
+init_semaphore_set ()
+{
+ // Ensure semaphore set file exists
+ gchar *key_file_name = g_build_filename (GVM_STATE_DIR, "gvmd.sem", NULL);
+ FILE *key_file = fopen (key_file_name, "a");
+ union semun sem_value;
+ if (key_file == NULL)
+ {
+ g_warning ("%s: error creating semaphore file %s: %s",
+ __func__, key_file_name, strerror (errno));
+ g_free (key_file_name);
+ return -1;
+ }
+ fclose (key_file);
+ semaphore_set_key = ftok (key_file_name, 0);
+ if (semaphore_set_key < 0)
+ {
+ g_warning ("%s: error creating semaphore key for file %s: %s",
+ __func__, key_file_name, strerror (errno));
+ g_free (key_file_name);
+ return -1;
+ }
+
+ semaphore_set = semget (semaphore_set_key, 1, 0660 | IPC_CREAT);
+ if (semaphore_set < 0)
+ {
+ g_warning ("%s: error getting semaphore set: %s",
+ __func__, strerror (errno));
+ g_free (key_file_name);
+ return -1;
+ }
+
+ g_debug ("%s: Semaphore set created for file '%s', key %x",
+ __func__, key_file_name, semaphore_set_key);
+ g_free (key_file_name);
+
+ sem_value.val = get_max_concurrent_scan_updates () ?: 1;
+ if (semctl (semaphore_set, SEMAPHORE_SCAN_UPDATE, SETVAL, sem_value) == -1)
+ {
+ g_warning ("%s: error initializing scan update semaphore: %s",
+ __func__, strerror (errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Performs a semaphore operation (signal or wait).
+ *
+ * A negative op_value will try to decrease the semaphore value
+ * and wait if needed.
+ * A positive op_value will increase the semaphore value.
+ * Zero as op_value will wait for the semaphore value to become zero.
+ *
+ * (See semop from sys/sem.h)
+ *
+ * @param[in] semaphore_index The index of the semaphore in the gvmd set.
+ * @param[in] op_value The operation value
+ * @param[in] timeout Timeout in seconds, 0 for unlimited
+ *
+ * @return 0 success, 1 timed out, -1 error
+ */
+int
+semaphore_op (semaphore_index_t semaphore_index,
+ short int op_value,
+ time_t timeout)
+{
+ int ret;
+ struct sembuf op = {
+ sem_num: semaphore_index,
+ sem_op: op_value,
+ sem_flg: SEM_UNDO
+ };
+
+ struct timespec ts = {
+ tv_nsec: 0,
+ tv_sec: timeout,
+ };
+
+ ret = semtimedop (semaphore_set, &op, 1, timeout > 0 ? &ts : NULL);
+ if (ret)
+ {
+ if (errno == EAGAIN)
+ return 1;
+ else
+ {
+ g_warning ("%s: semaphore operation failed: %s",
+ __func__, strerror (errno));
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/ipc.h b/src/ipc.h
new file mode 100644
index 000000000..e11371fdd
--- /dev/null
+++ b/src/ipc.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2024 Greenbone AG
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * @file ipc.h
+ * @brief Headers for inter-process communitcation (IPC)
+ */
+
+#ifndef _GVMD_IPC_H
+#define _GVMD_IPC_H
+
+typedef enum {
+ SEMAPHORE_SCAN_UPDATE = 0
+} semaphore_index_t;
+
+int
+init_semaphore_set ();
+
+int
+semaphore_op (semaphore_index_t, short int, time_t);
+
+#endif /* not _GVMD_IPC_H */
diff --git a/src/manage.c b/src/manage.c
index 5e4f754db..ce40a7000 100644
--- a/src/manage.c
+++ b/src/manage.c
@@ -48,6 +48,7 @@
#include "debug_utils.h"
#include "gmp_base.h"
+#include "ipc.h"
#include "manage.h"
#include "manage_acl.h"
#include "manage_configs.h"
@@ -184,6 +185,11 @@ static gchar *feed_lock_path = NULL;
*/
static int feed_lock_timeout = 0;
+/**
+ * @brief Maximum number of concurrent scan updates.
+ */
+static int max_concurrent_scan_updates = 0;
+
/**
* @brief Retries for waiting for memory to be available.
*/
@@ -1855,6 +1861,40 @@ get_osp_scan_status (const char *scan_id, const char *host, int port,
return status;
}
+/**
+ * @brief Handles the semaphore for the end of an OSP scan update.
+ *
+ * @param[in] add_result_on_error Whether to create an OSP result on error.
+ * @param[in] task The current task (for error result).
+ * @param[in] report The current report (for error result).
+ *
+ * @return 0 success, -1 error.
+ */
+static int
+osp_scan_semaphore_update_end (int add_result_on_error,
+ task_t task, report_t report)
+{
+ if (max_concurrent_scan_updates == 0)
+ return 0;
+
+ if (semaphore_op (SEMAPHORE_SCAN_UPDATE, +1, 0))
+ {
+ g_warning ("%s: error signaling scan update semaphore",
+ __func__);
+ if (add_result_on_error)
+ {
+ result_t result = make_osp_result
+ (task, "", "", "",
+ threat_message_type ("Error"),
+ "Error signaling scan update semaphore", "", "",
+ QOD_DEFAULT, NULL, NULL);
+ report_add_result (report, result);
+ }
+ return -1;
+ }
+ return 0;
+}
+
/**
* @brief Handle an ongoing OSP scan, until success or failure.
*
@@ -1888,7 +1928,7 @@ handle_osp_scan (task_t task, report_t report, const char *scan_id)
rc = -1;
while (retry >= 0)
{
- int run_status, progress;
+ int sem_op_ret, run_status, progress;
osp_scan_status_t osp_scan_status;
run_status = task_run_status (task);
@@ -1899,6 +1939,28 @@ handle_osp_scan (task_t task, report_t report, const char *scan_id)
break;
}
+ if (max_concurrent_scan_updates)
+ {
+ sem_op_ret = semaphore_op (SEMAPHORE_SCAN_UPDATE, -1, 5);
+ if (sem_op_ret == 1)
+ continue;
+ else if (sem_op_ret)
+ {
+ g_warning ("%s: error waiting for scan update semaphore",
+ __func__);
+ result_t result = make_osp_result
+ (task, "", "", "",
+ threat_message_type ("Error"),
+ "Error waiting for scan update semaphore", "", "",
+ QOD_DEFAULT, NULL, NULL);
+ report_add_result (report, result);
+ delete_osp_scan (scan_id, host, port, ca_pub, key_pub,
+ key_priv);
+ rc = -3;
+ break;
+ }
+ }
+
/* Get only the progress, without results and details. */
progress = get_osp_scan_report (scan_id, host, port, ca_pub, key_pub,
key_priv, 0, 0, NULL);
@@ -1911,10 +1973,18 @@ handle_osp_scan (task_t task, report_t report, const char *scan_id)
g_warning ("Connection lost with the scanner at %s. "
"Trying again in 1 second.", host);
gvm_sleep (1);
+ if (osp_scan_semaphore_update_end (TRUE, task, report))
+ {
+ delete_osp_scan (scan_id, host, port, ca_pub, key_pub,
+ key_priv);
+ rc = -3;
+ break;
+ }
continue;
}
else if (progress == -2)
{
+ osp_scan_semaphore_update_end (FALSE, task, report);
rc = -2;
break;
}
@@ -1924,6 +1994,7 @@ handle_osp_scan (task_t task, report_t report, const char *scan_id)
"Erroneous scan progress value", "", "",
QOD_DEFAULT, NULL, NULL);
report_add_result (report, result);
+ osp_scan_semaphore_update_end (FALSE, task, report);
delete_osp_scan (scan_id, host, port, ca_pub, key_pub,
key_priv);
rc = -1;
@@ -1942,11 +2013,19 @@ handle_osp_scan (task_t task, report_t report, const char *scan_id)
retry--;
g_warning ("Connection lost with the scanner at %s. "
"Trying again in 1 second.", host);
+ if (osp_scan_semaphore_update_end (TRUE, task, report))
+ {
+ delete_osp_scan (scan_id, host, port, ca_pub, key_pub,
+ key_priv);
+ rc = -3;
+ break;
+ }
gvm_sleep (1);
continue;
}
else if (progress == -2)
{
+ osp_scan_semaphore_update_end (FALSE, task, report);
rc = -2;
break;
}
@@ -1957,6 +2036,7 @@ handle_osp_scan (task_t task, report_t report, const char *scan_id)
"Erroneous scan progress value", "", "",
QOD_DEFAULT, NULL, NULL);
report_add_result (report, result);
+ osp_scan_semaphore_update_end (FALSE, task, report);
rc = -1;
break;
}
@@ -1989,6 +2069,7 @@ handle_osp_scan (task_t task, report_t report, const char *scan_id)
report_add_result (report, result);
delete_osp_scan (scan_id, host, port, ca_pub, key_pub,
key_priv);
+ osp_scan_semaphore_update_end (FALSE, task, report);
rc = -3;
break;
}
@@ -2000,6 +2081,13 @@ handle_osp_scan (task_t task, report_t report, const char *scan_id)
retry--;
g_warning ("Connection lost with the scanner at %s. "
"Trying again in 1 second.", host);
+ if (osp_scan_semaphore_update_end (TRUE, task, report))
+ {
+ delete_osp_scan (scan_id, host, port, ca_pub,
+ key_pub, key_priv);
+ rc = -3;
+ break;
+ }
gvm_sleep (1);
continue;
}
@@ -2012,6 +2100,7 @@ handle_osp_scan (task_t task, report_t report, const char *scan_id)
report_add_result (report, result);
delete_osp_scan (scan_id, host, port, ca_pub, key_pub,
key_priv);
+ osp_scan_semaphore_update_end (FALSE, task, report);
rc = -1;
break;
}
@@ -2020,6 +2109,7 @@ handle_osp_scan (task_t task, report_t report, const char *scan_id)
{
delete_osp_scan (scan_id, host, port, ca_pub, key_pub,
key_priv);
+ osp_scan_semaphore_update_end (FALSE, task, report);
rc = 0;
break;
}
@@ -2034,6 +2124,14 @@ handle_osp_scan (task_t task, report_t report, const char *scan_id)
}
}
+ if (osp_scan_semaphore_update_end (TRUE, task, report))
+ {
+ delete_osp_scan (scan_id, host, port, ca_pub, key_pub,
+ key_priv);
+ rc = -3;
+ break;
+ }
+
retry = connection_retry;
gvm_sleep (5);
}
@@ -2901,9 +2999,25 @@ fork_osp_scan_handler (task_t task, target_t target, int from,
set_task_run_status (task, TASK_STATUS_PROCESSING);
set_report_scan_run_status (global_current_report,
TASK_STATUS_PROCESSING);
+
+ if (max_concurrent_scan_updates)
+ semaphore_op (SEMAPHORE_SCAN_UPDATE, -1, 0);
hosts_set_identifiers (global_current_report);
+ if (max_concurrent_scan_updates)
+ semaphore_op (SEMAPHORE_SCAN_UPDATE, +1, 0);
+
+ if (max_concurrent_scan_updates)
+ semaphore_op (SEMAPHORE_SCAN_UPDATE, -1, 0);
hosts_set_max_severity (global_current_report, NULL, NULL);
+ if (max_concurrent_scan_updates)
+ semaphore_op (SEMAPHORE_SCAN_UPDATE, +1, 0);
+
+ if (max_concurrent_scan_updates)
+ semaphore_op (SEMAPHORE_SCAN_UPDATE, -1, 0);
hosts_set_details (global_current_report);
+ if (max_concurrent_scan_updates)
+ semaphore_op (SEMAPHORE_SCAN_UPDATE, +1, 0);
+
set_task_run_status (task, TASK_STATUS_DONE);
set_report_scan_run_status (global_current_report, TASK_STATUS_DONE);
}
@@ -6505,6 +6619,31 @@ set_min_mem_feed_update (int new_min_mem)
min_mem_feed_update = new_min_mem;
}
+/**
+ * @brief Get the maximum number of concurrent scan updates.
+ *
+ * @return The current maximum number of concurrent scan updates.
+ */
+int
+get_max_concurrent_scan_updates ()
+{
+ return max_concurrent_scan_updates;
+}
+
+/**
+ * @brief Set the maximum number of concurrent scan updates.
+ *
+ * @param new_max The new maximum number of concurrent scan updates.
+ */
+void
+set_max_concurrent_scan_updates (int new_max)
+{
+ if (new_max < 0)
+ max_concurrent_scan_updates = 0;
+ else
+ max_concurrent_scan_updates = new_max;
+}
+
/**
* @brief Write start time to sync lock file.
*
diff --git a/src/manage.h b/src/manage.h
index 6c59a32fc..0ee2e9c23 100644
--- a/src/manage.h
+++ b/src/manage.h
@@ -3880,6 +3880,12 @@ get_feed_lock_timeout ();
void
set_feed_lock_timeout (int);
+int
+get_max_concurrent_scan_updates ();
+
+void
+set_max_concurrent_scan_updates (int);
+
int
get_mem_wait_retries ();