From patchwork Mon Oct 3 15:27:19 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Robin Roevens X-Patchwork-Id: 6037 Return-Path: Received: from mail01.ipfire.org (mail01.haj.ipfire.org [172.28.1.202]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) client-signature ECDSA (P-384)) (Client CN "mail01.haj.ipfire.org", Issuer "R3" (verified OK)) by web04.haj.ipfire.org (Postfix) with ESMTPS id 4Mh6BV2sj3z3wcJ for ; Mon, 3 Oct 2022 16:44:50 +0000 (UTC) Received: from mail02.haj.ipfire.org (mail02.haj.ipfire.org [172.28.1.201]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) client-signature ECDSA (P-384)) (Client CN "mail02.haj.ipfire.org", Issuer "R3" (verified OK)) by mail01.ipfire.org (Postfix) with ESMTPS id 4Mh6BS0WmCz2RG; Mon, 3 Oct 2022 16:44:48 +0000 (UTC) Received: from mail02.haj.ipfire.org (localhost [127.0.0.1]) by mail02.haj.ipfire.org (Postfix) with ESMTP id 4Mh6BR6Tbkz2yWd; Mon, 3 Oct 2022 16:44:47 +0000 (UTC) Received: from mail01.ipfire.org (mail01.haj.ipfire.org [172.28.1.202]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) client-signature ECDSA (P-384)) (Client CN "mail01.haj.ipfire.org", Issuer "R3" (verified OK)) by mail02.haj.ipfire.org (Postfix) with ESMTPS id 4Mh6BQ65J5z2xRw for ; Mon, 3 Oct 2022 16:44:46 +0000 (UTC) Received: from knopi.disroot.org (knopi.disroot.org [178.21.23.139]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mail01.ipfire.org (Postfix) with ESMTPS id 4Mh6BQ2fvMz1Pk for ; Mon, 3 Oct 2022 16:44:46 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by disroot.org (Postfix) with ESMTP id C5EFE4C702 for ; Mon, 3 Oct 2022 18:44:45 +0200 (CEST) X-Virus-Scanned: SPAM Filter at disroot.org Received: from knopi.disroot.org ([127.0.0.1]) by localhost (disroot.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 3AjIhE7afHMc for ; Mon, 3 Oct 2022 18:44:43 +0200 (CEST) Received: from chojin.sicho.home (amaterasu.sicho.home [192.168.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (no client certificate requested) (Authenticated sender) by hachiman (MailScanner Milter) with SMTP id B4B0471E35; Mon, 3 Oct 2022 17:27:49 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=disroot.org; s=mail; t=1664815481; bh=A1R2rXf6ez0eUhiBiUj2pgv8xJ6MZwfdvfnvQXyfEfg=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=Dtcc99/09M4+Pi+v/LfoKG9EZdoI687bZ4UXHSNU87pQfHh2nPN0kS7KN/Gkwz3ji eVRrH1ctr6v0oEUViVVy4FihcEpYQKsVdOEXEPD6qEJl5dkKLzqy7YLLppX/yFFbFS Lkc3tCmIvxzKnVAVDogQGIe014uceT9DLBRIMmUXKFuOPqUtjAiIK4Hr1tZJjLIE2h nmMth9IZ9WDmRIjVeT1chKKTCaMIUdYi/Hz1GOw07z4QXlbupDlfoDdb6Jm+PwWQJz 4oYK6aZoRNC+1Mi2rcm9rhS0B0ZF9UoJvaZ3lQZE6lbKMWRrF04s7hd5kjA51EVTCb mxeDNEEPkt7tQ== From: Robin Roevens To: development@lists.ipfire.org Subject: [PATCH 1/2] misc-progs: addonctrl: Add support for 'Services' metadata Date: Mon, 3 Oct 2022 17:27:19 +0200 Message-Id: <20221003152720.13140-3-robin.roevens@disroot.org> In-Reply-To: <20221003152720.13140-1-robin.roevens@disroot.org> References: <20221003152720.13140-1-robin.roevens@disroot.org> Mime-Version: 1.0 X-sicho-MailScanner-ID: B4B0471E35.A8A80 X-sicho-MailScanner: Found to be clean X-sicho-MailScanner-From: robin.roevens@disroot.org X-sicho-MailScanner-Watermark: 1665415669.91726@t9pkzH/avvadaR2B9Ul3uw ARC-Seal: i=1; s=202003rsa; d=lists.ipfire.org; t=1664815486; a=rsa-sha256; cv=none; b=C8/jDM2clik2Pa9WmoG949zUAi6aVoir/HtKQ6QqKakhFRQdpXcPLvbYHkjyMU41XdAwjA jTaxhnnW424rYnYJ1Lv7tCPnE/3HV1EHYtFJ4u1kMW59Cj6dL7f5saKjXFUvmyc03RtftF +u7xZoqZ8wubcIAdnpAcOLwX6+rHZ0CGuBNkPNcPnwuLidUr5xcwQE1xZLl10DyJVoBz8p X1m8Ex4Vfw9GSrTNxTAKTN51aRdQl4rlcSVi9lWTUdBueq/0bM9MyNUtJMORSfxZgZ2UC+ drVAnu5PdNh2PQ69XZp+B3dxPi0wLZZjnjnHuqRAvzl0IzxNXeZ+PZ9lHZ+UfA== ARC-Authentication-Results: i=1; mail01.ipfire.org; dkim=pass header.d=disroot.org header.s=mail header.b="Dtcc99/0"; spf=pass (mail01.ipfire.org: domain of robin.roevens@disroot.org designates 178.21.23.139 as permitted sender) smtp.mailfrom=robin.roevens@disroot.org; dmarc=pass (policy=quarantine) header.from=disroot.org ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=lists.ipfire.org; s=202003rsa; t=1664815486; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:dkim-signature; bh=eqG5zps9sMmlShxHNcMMbxRJi4Zia8QaGduT02G6TG0=; b=slwH+iZQeAWIg43UA8/2uG2UutWEc43wwhDvqxYw7mYlCK/WJObS5TC50QFUv3k1KqDIFG yii9xM/HxAy8BTBCzGwbvDgnAcmvHNbi+MJ+OCOzzbpbXpWT6Uc9ntuQkWXp/TJ6C/+iO2 5copIX9KRWrIPhzkgwfPFdQ3p6vcAPiI8KGOGQBydhI2zpFo95TacqV/oJdSKB54vWiQfy oeLRjZcBrBrvOrXPZjbzohmieK9UwaYuc14zW6330sV1ossF3DMmF7j6Q2o8hXv+rnN/jc nQktBo0lRAwOlvFnRR7Utn5s+bQ9iN0G4TB1uit6GUXsQ/3/+02kcz9ycWYBig== Authentication-Results: mail01.ipfire.org; dkim=pass header.d=disroot.org header.s=mail header.b="Dtcc99/0"; spf=pass (mail01.ipfire.org: domain of robin.roevens@disroot.org designates 178.21.23.139 as permitted sender) smtp.mailfrom=robin.roevens@disroot.org; dmarc=pass (policy=quarantine) header.from=disroot.org X-Rspamd-Server: mail01.haj.ipfire.org X-Spamd-Result: default: False [-5.05 / 11.00]; BAYES_HAM(-3.00)[99.99%]; IP_REPUTATION_HAM(-1.07)[asn: 50673(-0.30), country: NL(-0.01), ip: 178.21.23.139(-0.76)]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM(-1.00)[-1.000]; DKIM_REPUTATION(-0.77)[-0.76738082578851]; SPF_REPUTATION_HAM(-0.67)[-0.66696041144247]; DMARC_POLICY_ALLOW(-0.50)[disroot.org,quarantine]; R_MISSING_CHARSET(0.50)[]; MV_CASE(0.50)[]; R_DKIM_ALLOW(0.26)[disroot.org:s=mail]; R_SPF_ALLOW(-0.20)[+a:c]; MIME_GOOD(-0.10)[text/plain]; MX_GOOD(-0.01)[]; ARC_NA(0.00)[]; PREVIOUSLY_DELIVERED(0.00)[development@lists.ipfire.org]; FROM_HAS_DN(0.00)[]; TO_MATCH_ENVRCPT_SOME(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; RCVD_TLS_LAST(0.00)[]; RCVD_COUNT_THREE(0.00)[4]; ARC_SIGNED(0.00)[lists.ipfire.org:s=202003rsa:i=1]; ASN(0.00)[asn:50673, ipnet:178.21.23.0/24, country:NL]; DKIM_TRACE(0.00)[disroot.org:+]; MIME_TRACE(0.00)[0:+]; RCPT_COUNT_TWO(0.00)[2]; TO_DN_SOME(0.00)[] X-Rspamd-Queue-Id: 4Mh6BQ2fvMz1Pk X-BeenThere: development@lists.ipfire.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: IPFire development talk List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: development-bounces@lists.ipfire.org Sender: "Development" * Addonctrl will now check in addon metadata for the exact initscript names (Services). If more than one initscript is defined for an addon, the requested action will be performed on all listed initscripts. * Added posibility to perform action on a specific initscript of an addon instead of on all initscripts of the addon. * New action 'list-services' to display a list of services related to an addon. * New action 'boot-status' to display wether service(s) are enabled to start on boot or not. * More error checking and cleaner error reporting to user * General cleanup and code restructuring to avoid code duplication * Updated and made usage instructions more verbose. Fixes: Bug#12935 Signed-off-by: Robin Roevens Tested-by: Adolf Belka --- src/misc-progs/addonctrl.c | 384 +++++++++++++++++++++++++++++++------ 1 file changed, 323 insertions(+), 61 deletions(-) diff --git a/src/misc-progs/addonctrl.c b/src/misc-progs/addonctrl.c index 14b4b1325..277bd1809 100644 --- a/src/misc-progs/addonctrl.c +++ b/src/misc-progs/addonctrl.c @@ -10,71 +10,333 @@ #include #include #include +#include #include +#include +#include #include "setuid.h" #define BUFFER_SIZE 1024 +const char *enabled_path = "/etc/rc.d/rc3.d"; +const char *disabled_path = "/etc/rc.d/rc3.d/off"; + +char errormsg[BUFFER_SIZE] = ""; +char *usage = + "Usage\n" + " addonctrl (start|stop|restart|reload|enable|disable|status|boot-status|list-services) []\n" + "\n" + "Options:\n" + " \t\tName of the addon to control\n" + " \t\tSpecific service of the addon to control (optional)\n" + " \t\t\tBy default the requested action is performed on all related services. See also 'list-services'.\n" + " start\t\t\tStart service(s) of the addon\n" + " stop\t\t\tStop service(s) of the addon\n" + " restart\t\tRestart service(s) of the addon\n" + " enable\t\tEnable service(s) of the addon to start at boot\n" + " disable\t\tDisable service(s) of the addon to start at boot\n" + " status\t\tDisplay current state of addon service(s)\n" + " boot-status\t\tDisplay wether service(s) is enabled on boot or not\n" + " list-services\t\tDisplay a list of services related to the addon"; + +// Check if matches . Wildcard '?' matches any single character. +// Returns 1 when found, 0 when not found +int match(const char *pattern, const char *text) { + if (pattern[0] == '\0' && *text == '\0') + return 1; + if (*text != '\0' && (pattern[0]=='?' || pattern[0]==*text)) + return match(pattern+1, text+1); + return 0; +} + +// Find a file using allowing the '?' wildcard. +// Returns the found filename or NULL if not found +char* find_file_in_dir(const char *path, const char *filepattern) +{ + static struct dirent *entry; + DIR *dp; + int found = 0; + + if ((dp = opendir(path)) != NULL) { + while(found == 0 && (entry = readdir(dp)) != NULL) + found = match(filepattern, entry->d_name); + + closedir(dp); + } + + if (! found) { + return NULL; + } + + return entry->d_name; +} + +// Reads Services metadata for . +// Returns pointer to array of strings containing the services for +// and sets to the number of found services +char **get_addon_services(const char *addon, int *servicescnt) { + const char *metafile_prefix = "/opt/pakfire/db/installed/meta-"; + const char *metadata_key = "Services"; + const char *keyvalue_delim = ":"; + const char *service_delim = " "; + char *token; + char **services = NULL; + char *service; + char line[BUFFER_SIZE]; + int i = 0; + char *metafile = malloc((strlen(metafile_prefix) + strlen(addon) + 1) * sizeof(char)); + + sprintf(metafile, "%s%s", metafile_prefix, addon); + FILE *fp = fopen(metafile,"r"); + if ( fp ) { + // Get initscript(s) for addon from meta-file + while (!feof(fp) && services == NULL) { + if (fgets(line, BUFFER_SIZE - 1, fp) != NULL) { + // Strip newline + char *newline = strchr(line, '\n'); + if (newline) *newline = 0; + + // Parse key/value and look for required key. + token = strtok(line, keyvalue_delim); + if (token != NULL && strcmp(token, metadata_key) == 0) { + token = strtok(NULL, keyvalue_delim); + if (token != NULL) { + services = malloc((strlen(token) + 1) * sizeof (char *)); + + // Put each service in services array + service = strtok(token, service_delim); + while (service != NULL) { + services[i] = malloc((strlen(service) + 1) * sizeof (char)); + strcpy(services[i++], service); + + service = strtok(NULL, service_delim); + } + } + } + } else { + snprintf(errormsg, BUFFER_SIZE - 1, "Could not read '%s' metadata for addon '%s'.", metadata_key, addon); + } + } + fclose(fp); + } else { + snprintf(errormsg, BUFFER_SIZE - 1, "Addon '%s' not found.\n\n%s", addon, usage); + } + + free (metafile); + *servicescnt = i; + return services; +} + +// Calls initscript with parameter +int initscript_action(const char *service, const char *action) { + const char *initd_path = "/etc/rc.d/init.d"; + char *command = malloc((strlen(initd_path) + 1 + strlen(service) + 1) * sizeof(char)); + int r = 0; + + sprintf(command, "%s/%s %s", initd_path, service, action); + r = safe_system(command); + free(command); + + return r; +} + +// Move an initscript with filepattern from to +// Returns: +// -1: Error during move. Details in errno (returned by C rename function) +// 0: Success +// 1: file was not moved, but is already in +// 2: file does not exist in either in or +int move_initscript_by_pattern(const char *src_path, const char *dest_path, const char *filepattern) { + char *src, *dest; + int r = 1; + char *filename = find_file_in_dir(src_path, filepattern); + + if ( filename != NULL ) { + src = malloc((strlen(src_path) + 1 + strlen(filename) + 1) * sizeof(char)); + dest = malloc((strlen(dest_path) + 1 + strlen(filename) + 1) * sizeof(char)); + sprintf(src, "%s/%s", src_path, filename); + sprintf(dest, "%s/%s", dest_path, filename); + + r = rename(src, dest); + + free(src); + free(dest); + } else { + filename = find_file_in_dir(dest_path, filepattern); + if (filename == NULL) + r = 2; + } + + return r; +} + +// Enable/Disable addon service(s) by moving initscript symlink from/to disabled_path +int toggle_service(const char *service, const char *action) { + const char *src_path, *dest_path; + char *filepattern = malloc((3 + strlen(service) + 1) * sizeof(char)); + int r = 0; + + sprintf(filepattern, "S??%s", service); + + if (strcmp(action, "enable") == 0) { + src_path = disabled_path; + dest_path = enabled_path; + } else { + src_path = enabled_path; + dest_path = disabled_path; + } + + // Ensure disabled_path exists + if (mkdir(disabled_path, S_IRWXU + S_IRGRP + S_IXGRP + S_IROTH + S_IXOTH) == -1 && errno != EEXIST) { + r = 1; + snprintf(errormsg, BUFFER_SIZE -1, "Error creating %s. (Error: %d)", disabled_path, errno); + } else { + r = move_initscript_by_pattern(src_path, dest_path, filepattern); + if (r == -1 ) { + r = 1; + snprintf(errormsg, BUFFER_SIZE - 1, "Could not %s %s. (Error: %d)", action, service, errno); + } else if (r == 1) { + snprintf(errormsg, BUFFER_SIZE - 1, "Service %s is already %sd. Skipping...", service, action); + } else if (r == 2) { + snprintf(errormsg, BUFFER_SIZE - 1, "Unable to %s service %s. (Service has no valid symlink in %s).", action, service, src_path); + } + } + + free(filepattern); + + return r; +} + +// Print to stdout wether is enabled or disabled on boot +// Prints as Not available when initscript is not found +// in either enabled_path or disabled_path. +void print_boot_status(char *service) { + char *filepattern = malloc((3 + strlen(service) + 1) * sizeof(char)); + sprintf(filepattern, "S??%s", service); + char *enabled = find_file_in_dir(enabled_path, filepattern); + char *disabled = find_file_in_dir(disabled_path, filepattern); + + if (enabled != NULL) + fprintf(stdout, "%s is enabled on boot.\n", service); + else if (disabled != NULL) + fprintf(stdout, "%s is disabled on boot.\n", service); + else + fprintf(stdout, "%s is not available for boot. (Service has no valid symlink in either %s or %s).\n", service, enabled_path, disabled_path); + + free(filepattern); +} + int main(int argc, char *argv[]) { - char command[BUFFER_SIZE]; - - if (!(initsetuid())) - exit(1); - - if (argc < 3) { - fprintf(stderr, "\nMissing arguments.\n\naddonctrl addon (start|stop|restart|reload|enable|disable)\n\n"); - exit(1); - } - - const char* name = argv[1]; - - if (strlen(name) > 32) { - fprintf(stderr, "\nString to large.\n\naddonctrl addon (start|stop|restart|reload|enable|disable)\n\n"); - exit(1); - } - - // Check if the input argument is valid - if (!is_valid_argument_alnum(name)) { - fprintf(stderr, "Invalid add-on name: %s\n", name); - exit(2); - } - - sprintf(command, "/opt/pakfire/db/installed/meta-%s", name); - FILE *fp = fopen(command,"r"); - if ( fp ) { - fclose(fp); - } else { - fprintf(stderr, "\nAddon '%s' not found.\n\naddonctrl addon (start|stop|restart|reload|status|enable|disable)\n\n", name); - exit(1); - } - - if (strcmp(argv[2], "start") == 0) { - snprintf(command, BUFFER_SIZE - 1, "/etc/rc.d/init.d/%s start", name); - safe_system(command); - } else if (strcmp(argv[2], "stop") == 0) { - snprintf(command, BUFFER_SIZE - 1, "/etc/rc.d/init.d/%s stop", name); - safe_system(command); - } else if (strcmp(argv[2], "restart") == 0) { - snprintf(command, BUFFER_SIZE - 1, "/etc/rc.d/init.d/%s restart", name); - safe_system(command); - } else if (strcmp(argv[2], "reload") == 0) { - snprintf(command, BUFFER_SIZE - 1, "/etc/rc.d/init.d/%s reload", name); - safe_system(command); - } else if (strcmp(argv[2], "status") == 0) { - snprintf(command, BUFFER_SIZE - 1, "/etc/rc.d/init.d/%s status", name); - safe_system(command); - } else if (strcmp(argv[2], "enable") == 0) { - snprintf(command, BUFFER_SIZE - 1, "mv -f /etc/rc.d/rc3.d/off/S??%s /etc/rc.d/rc3.d" , name); - safe_system(command); - } else if (strcmp(argv[2], "disable") == 0) { - snprintf(command, BUFFER_SIZE - 1, "mkdir -p /etc/rc.d/rc3.d/off"); - safe_system(command); - snprintf(command, BUFFER_SIZE - 1, "mv -f /etc/rc.d/rc3.d/S??%s /etc/rc.d/rc3.d/off" , name); - safe_system(command); - } else { - fprintf(stderr, "\nBad argument given.\n\naddonctrl addon (start|stop|restart|reload|enable|disable)\n\n"); - exit(1); - } - - return 0; + char **services = NULL; + char **services_ptr = NULL; + int servicescnt = 0; + char *addon = argv[1]; + char *action = argv[2]; + char *service_filter = NULL; + int actioned = 0; + int r = 0; + + if (!(initsetuid())) + exit(1); + + if (argc < 3) { + fprintf(stderr, "\nMissing arguments.\n\n%s\n\n", usage); + exit(1); + } + + if (argc == 4) + service_filter = argv[3]; + + if (strlen(addon) > 32) { + fprintf(stderr, "\nString to large.\n\n%s\n\n", usage); + exit(1); + } + + // Check if the input argument is valid + if (!is_valid_argument_alnum(addon)) { + fprintf(stderr, "Invalid add-on name: %s.\n", addon); + exit(2); + } + + // Get initscript name(s) from addon metadata + services_ptr = get_addon_services(addon, &servicescnt); + services = services_ptr; + if (services == NULL || *services == 0) { + if (strcmp(errormsg, "") != 0) + fprintf(stderr, "\n%s\n\n", errormsg); + else + fprintf(stderr, "\nAddon '%s' has no services.\n\n", addon); + exit(1); + } + + if (strcmp(action, "start") == 0 || + strcmp(action, "stop") == 0 || + strcmp(action, "restart") == 0 || + strcmp(action, "reload") == 0 || + strcmp(action, "status") == 0) { + + while (*services != 0) { + if ((service_filter != NULL && strcmp(service_filter, *services) == 0) || + service_filter == NULL) { + if (initscript_action(*services, action) != 0) + r = 1; + ++actioned; + } + ++services; + } + + } else if (strcmp(action, "enable") == 0 || + strcmp(action, "disable") == 0) { + + while (*services != 0) { + if ((service_filter != NULL && strcmp(service_filter, *services) == 0) || + service_filter == NULL) { + if (toggle_service(*services, action) == 0) { + fprintf(stdout, "%sd service %s\n", action, *services); + } + else { + r = 1; + fprintf(stderr, "\n%s\n\n", errormsg); + } + + ++actioned; + } + ++services; + } + + } else if (strcmp(action, "boot-status") == 0) { + while(*services != 0) { + if ((service_filter != NULL && strcmp(service_filter, *services) == 0) || + service_filter == NULL) { + print_boot_status(*services); + ++actioned; + } + ++services; + } + + } else if (strcmp(action, "list-services") == 0) { + fprintf(stdout, "\nServices for addon %s:\n", addon); + while (*services != 0) { + fprintf(stdout, " %s\n", *services); + ++actioned; + ++services; + } + fprintf(stdout, "\n"); + + } else { + fprintf(stderr, "\nBad argument given.\n\n%s\n\n", usage); + r = 1; + } + + if (r == 0 && service_filter != NULL && actioned == 0) { + fprintf(stderr, "\nNo service %s found for addon %s. Use 'list-services' to get a list of available services\n\n%s\n\n", service_filter, addon, usage); + r = 1; + } + + // Cleanup + for(int i = 0; i < servicescnt; i++) + free(services_ptr[i]); + free(services_ptr); + + return r; } From patchwork Mon Oct 3 15:27:20 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Robin Roevens X-Patchwork-Id: 6038 Return-Path: Received: from mail01.ipfire.org (mail01.haj.ipfire.org [172.28.1.202]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) client-signature ECDSA (P-384)) (Client CN "mail01.haj.ipfire.org", Issuer "R3" (verified OK)) by web04.haj.ipfire.org (Postfix) with ESMTPS id 4Mh6BV71Xjz3wg5 for ; Mon, 3 Oct 2022 16:44:50 +0000 (UTC) Received: from mail02.haj.ipfire.org (mail02.haj.ipfire.org [172.28.1.201]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) client-signature ECDSA (P-384)) (Client CN "mail02.haj.ipfire.org", Issuer "R3" (verified OK)) by mail01.ipfire.org (Postfix) with ESMTPS id 4Mh6BT0v7Mz2BD; Mon, 3 Oct 2022 16:44:49 +0000 (UTC) Received: from mail02.haj.ipfire.org (localhost [127.0.0.1]) by mail02.haj.ipfire.org (Postfix) with ESMTP id 4Mh6BT04K4z2xn3; Mon, 3 Oct 2022 16:44:49 +0000 (UTC) Received: from mail01.ipfire.org (mail01.haj.ipfire.org [172.28.1.202]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) client-signature ECDSA (P-384)) (Client CN "mail01.haj.ipfire.org", Issuer "R3" (verified OK)) by mail02.haj.ipfire.org (Postfix) with ESMTPS id 4Mh6BR5YMgz2xg5 for ; Mon, 3 Oct 2022 16:44:47 +0000 (UTC) Received: from knopi.disroot.org (knopi.disroot.org [178.21.23.139]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mail01.ipfire.org (Postfix) with ESMTPS id 4Mh6BR4Rwlz1Ls for ; Mon, 3 Oct 2022 16:44:47 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by disroot.org (Postfix) with ESMTP id 4B04C4C701 for ; Mon, 3 Oct 2022 18:44:47 +0200 (CEST) X-Virus-Scanned: SPAM Filter at disroot.org Received: from knopi.disroot.org ([127.0.0.1]) by localhost (disroot.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id MG-Fss8cIjr7 for ; Mon, 3 Oct 2022 18:44:45 +0200 (CEST) Received: from chojin.sicho.home (amaterasu.sicho.home [192.168.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (no client certificate requested) (Authenticated sender) by hachiman (MailScanner Milter) with SMTP id 9D43671E55; Mon, 3 Oct 2022 17:27:52 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=disroot.org; s=mail; t=1664815481; bh=2SeonbATKstyOvVX8yF4ATJuLGlpQyBScyjkpfMVhsQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=gJCbFnmTGgnQuFTBpfXuCji4f8vFgYzHgErkbAGxHe+LKRgliVoSWUVZQiV3Cedon /Z9cLkRAX3q8cCK7RdFUEatH/YFZtSRgZKRe2W0r3EaEqDP5ugsBu/VxD15FSpT2f9 0UhuESyWlweNmSbk9pECtprWuVSYjSo4b3+dSprMFkmMdXC/GDOc1euQnBlnswQmRE IifX2rxQMkU5BiOSR3NQSgUfGikRrtCLT8fmzgkCCLjIs/jqW3F5cDsUxhj4L2+Qwp qh1QSS5UZA1TWiv6wwXB2i2x0TThOEuK4NJg4SEJI9NkKz8eJ8eYAQPorXncWJUUNy /S/Jhp/JEQGxw== From: Robin Roevens To: development@lists.ipfire.org Subject: [PATCH 2/2] services.cgi: Fix status/actions on services with name != addon name Date: Mon, 3 Oct 2022 17:27:20 +0200 Message-Id: <20221003152720.13140-4-robin.roevens@disroot.org> In-Reply-To: <20221003152720.13140-1-robin.roevens@disroot.org> References: <20221003152720.13140-1-robin.roevens@disroot.org> Mime-Version: 1.0 X-sicho-MailScanner-ID: 9D43671E55.A8A80 X-sicho-MailScanner: Found to be clean X-sicho-MailScanner-From: robin.roevens@disroot.org X-sicho-MailScanner-Watermark: 1665415672.75163@kqA2OcbQmSHxZhgOULsTGg ARC-Seal: i=1; s=202003rsa; d=lists.ipfire.org; t=1664815487; a=rsa-sha256; cv=none; b=aXx8Wv920ZLB9BOpGNDTA5NcQ48PJFs6BbLuFqS4YN32rlb8wayv4oMkFr2Df1wrQEaIpU KRBG4wVqTC1WSsrUqyqvOX40YEn1fvQZODr5PZcYfiTFFYBQeZNYX+e108cvYjr7Q/45vr +evjubeXanvzbc1PFpVS/XUvW4vK597Fn3mBUAK9zh0QVtlknPGUK3Pnd9a2u2TniUBpER WOq81mfTgEJV6ksRlPDCvmqNFForc5DROcxC9RGx8sa4yBQrR3BF3yZn3xUBgfq7DlnKKe WHQwQgVfVIKr6oIbaRG40KSltFllhq4ciSQYSKoJdATyYHw9kKPIoiDj1liyfw== ARC-Authentication-Results: i=1; mail01.ipfire.org; dkim=pass header.d=disroot.org header.s=mail header.b=gJCbFnmT; spf=pass (mail01.ipfire.org: domain of robin.roevens@disroot.org designates 178.21.23.139 as permitted sender) smtp.mailfrom=robin.roevens@disroot.org; dmarc=pass (policy=quarantine) header.from=disroot.org ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=lists.ipfire.org; s=202003rsa; t=1664815487; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:dkim-signature; bh=6grr7KggdhC/Km6vNwAZ9BuuBW/lQ88VmaZIt//hheY=; b=Avj+FpX3/ShOMy8mOw+Foy4FQIeXu9pPG7XJb3nyoNwsijxmgsRKHtbaYNby0gIsBQ/Svg loFyRSYvLDnOw5zwEt4Ee0HX4De2eiott59i8JoXeFG92S15cKFnBqIp52sl0MedYYDEUj Z6x1QgqQvQfWLUgcP964D//24RZajrrrZjJdOmusOTflTJp5UazjgEjfbCdnLiA61olXPp 3lJ2qW79cVp+c65+92LTRaFrYEWVSkKsGM/GkhwwHCSxeiAE2OzVHZpFxaSiSZiROxFJCh 26718+i9ASEwVVfPQAjAulP5BUbJR3oS0sQDGvd9TQHH3tUZBVl/6+ldN8MbEA== Authentication-Results: mail01.ipfire.org; dkim=pass header.d=disroot.org header.s=mail header.b=gJCbFnmT; spf=pass (mail01.ipfire.org: domain of robin.roevens@disroot.org designates 178.21.23.139 as permitted sender) smtp.mailfrom=robin.roevens@disroot.org; dmarc=pass (policy=quarantine) header.from=disroot.org X-Rspamd-Server: mail01.haj.ipfire.org X-Spamd-Result: default: False [-4.31 / 11.00]; BAYES_HAM(-3.00)[99.99%]; IP_REPUTATION_HAM(-1.07)[asn: 50673(-0.30), country: NL(-0.01), ip: 178.21.23.139(-0.76)]; MID_CONTAINS_FROM(1.00)[]; DKIM_REPUTATION(-0.77)[-0.76738082578851]; SPF_REPUTATION_HAM(-0.67)[-0.66696041144247]; DMARC_POLICY_ALLOW(-0.50)[disroot.org,quarantine]; MV_CASE(0.50)[]; R_DKIM_ALLOW(0.26)[disroot.org:s=mail]; MIME_HTML_ONLY(0.20)[]; R_SPF_ALLOW(-0.20)[+a:c]; NEURAL_HAM(-0.06)[-0.062]; MX_GOOD(-0.01)[]; ARC_NA(0.00)[]; FROM_HAS_DN(0.00)[]; TO_MATCH_ENVRCPT_SOME(0.00)[]; PREVIOUSLY_DELIVERED(0.00)[development@lists.ipfire.org]; FROM_EQ_ENVFROM(0.00)[]; RCVD_TLS_LAST(0.00)[]; RCVD_COUNT_THREE(0.00)[4]; ARC_SIGNED(0.00)[lists.ipfire.org:s=202003rsa:i=1]; ASN(0.00)[asn:50673, ipnet:178.21.23.0/24, country:NL]; DKIM_TRACE(0.00)[disroot.org:+]; RCPT_COUNT_TWO(0.00)[2]; SUBJECT_HAS_EXCLAIM(0.00)[]; MIME_TRACE(0.00)[0:~]; TO_DN_SOME(0.00)[] X-Rspamd-Queue-Id: 4Mh6BR4Rwlz1Ls X-BeenThere: development@lists.ipfire.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: IPFire development talk List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: development-bounces@lists.ipfire.org Sender: "Development" * addonctrl's new functionality to control explicit addon services was implemented. * Change 'Addon' column header to 'Addon Service' to be clear that it's not addons but services listed here. * Services not matching the name of the addon now display the addon name between parentheses, so the user knows where the service comes from. * When no valid runlevel symlink is found by addonctrl for a service, the 'enable on boot' checkbox is replaced by a small exclamation point with alt-text "No valid runlevel symlink was found for the initscript of this service." to inform user why a service can't be enabled. * Added German and Dutch translation for above message. Fixes: Bug#12935 Signed-off-by: Robin Roevens Tested-by: Adolf Belka --- html/cgi-bin/services.cgi | 103 +++++++++++++++----------------------- langs/de/cgi-bin/de.pl | 1 + langs/en/cgi-bin/en.pl | 1 + langs/nl/cgi-bin/nl.pl | 1 + 4 files changed, 42 insertions(+), 64 deletions(-) diff --git a/html/cgi-bin/services.cgi b/html/cgi-bin/services.cgi index 29926ecc3..e91da884b 100644 --- a/html/cgi-bin/services.cgi +++ b/html/cgi-bin/services.cgi @@ -142,14 +142,14 @@ END my $paramstr=$ENV{QUERY_STRING}; my @param=split(/!/, $paramstr); if ($param[1] ne ''){ - &General::system("/usr/local/bin/addonctrl", "$param[0]", "$param[1]"); + &General::system("/usr/local/bin/addonctrl", "$param[0]", "$param[1]", "$param[2]"); } print < - + @@ -170,33 +170,35 @@ END foreach my $pak (keys %paklist) { my %metadata = &Pakfire::getmetadata($pak, "installed"); + my $service; + if ("$metadata{'Services'}") { - foreach my $service (split(/ /, "$metadata{'Services'}")) { - push(@addon_services, $service); - } - } - } + foreach $service (split(/ /, "$metadata{'Services'}")) { + $lines++; + if ($lines % 2) { + print ""; + $col="bgcolor='$color{'color22'}'"; + } else { + print ""; + $col="bgcolor='$color{'color20'}'"; + } - foreach (@addon_services) { - $lines++; - if ($lines % 2){ - print ""; - $col="bgcolor='$color{'color22'}'"; - }else{ - print ""; - $col="bgcolor='$color{'color20'}'"; + # Add addon name to displayname of service if servicename differs from addon + my $displayname = ($pak ne $service) ? "$service ($pak)" : $service; + print " "; + + my $status = isautorun($pak,$service,$col); + print "$status "; + print ""; + print " "; + my $status = isrunningaddon($pak,$service,$col); + $status =~ s/\\[[0-1]\;[0-9]+m//g; + + chomp($status); + print "$status"; + print ""; + } } - print " "; - my $status = isautorun($_,$col); - print "$status "; - print ""; - print " "; - my $status = isrunningaddon($_,$col); - $status =~ s/\\[[0-1]\;[0-9]+m//g; - - chomp($status); - print "$status"; - print ""; } print "
AddonAddon $Lang::tr{service} Boot $Lang::tr{'action'} $Lang::tr{'status'}
$displayname$Lang::tr{$Lang::tr{
$_$Lang::tr{$Lang::tr{
\n"; @@ -215,51 +217,24 @@ END } sub isautorun (@) { - my ($cmd, $col) = @_; - - # Init directory. - my $initdir = "/etc/rc.d/rc3.d/"; - - my $status = ""; + my ($pak, $service, $col) = @_; + my @testcmd = &General::system_output("/usr/local/bin/addonctrl", "$pak", "boot-status", "$service"); + my $testcmd = @testcmd[0]; + my $status = "$Lang::tr{"; - # Check if autorun for the given cmd is enabled. - if ( &find_init("$cmd", "$initdir") ) { + # Check if autorun for the given service is enabled. + if ( $testcmd =~ /enabled\ on\ boot/ ) { # Adjust status. - $status = "$Lang::tr{"; - } else { + $status = "$Lang::tr{"; + } elsif ( $testcmd =~ /disabled\ on\ boot/ ) { # Adjust status. - $status = "$Lang::tr{"; + $status = "$Lang::tr{"; } # Return the status. return $status; } -sub find_init (@) { - my ($cmd, $dir) = @_; - - # Open given init directory. - opendir (INITDIR, "$dir") || die "Cannot opendir $dir: $!"; - - # Read-in init files from directory. - my @inits = readdir(INITDIR); - - # Close directory handle. - closedir(INITDIR); - - # Loop through the directory. - foreach my $init (@inits) { - # Check if the current processed file belongs to the given command. - if ($init =~ /S\d+\d+$cmd\z/) { - # Found, return "1" - True. - return "1"; - } - } - - # Nothing found, return nothing. - return; -} - sub isrunning (@) { my ($cmd, $col) = @_; my $status = "$Lang::tr{'stopped'}"; @@ -313,7 +288,7 @@ sub isrunning (@) { } sub isrunningaddon (@) { - my ($cmd, $col) = @_; + my ($pak, $service, $col) = @_; my $status = "$Lang::tr{'stopped'}"; my $pid = ''; @@ -321,7 +296,7 @@ sub isrunningaddon (@) { my $exename; my @memory; - my @testcmd = &General::system_output("/usr/local/bin/addonctrl", "$cmd", "status"); + my @testcmd = &General::system_output("/usr/local/bin/addonctrl", "$pak", "status", "$service"); my $testcmd = @testcmd[0]; if ( $testcmd =~ /is\ running/ && $testcmd !~ /is\ not\ running/){ diff --git a/langs/de/cgi-bin/de.pl b/langs/de/cgi-bin/de.pl index 798abcffc..db7d117b0 100644 --- a/langs/de/cgi-bin/de.pl +++ b/langs/de/cgi-bin/de.pl @@ -2251,6 +2251,7 @@ 'server string' => 'Server String', 'service' => 'Dienst', 'service added' => 'Benutzerdefinierter Netzwerkdienst wurde hinzugefügt', +'service boot setting unavailable' => 'Für das Initscript dieses Dienstes wurde kein gültiger Runlevel-Symlink gefunden.', 'service name' => 'Name des Dienstes:', 'service removed' => 'Benutzerdefinierter Netzwerkdienst wurde entfernt', 'service updated' => 'Benutzerdefinierter Netzwerkdienst wurde aktualisiert', diff --git a/langs/en/cgi-bin/en.pl b/langs/en/cgi-bin/en.pl index f770e7cd9..60dca5be4 100644 --- a/langs/en/cgi-bin/en.pl +++ b/langs/en/cgi-bin/en.pl @@ -2306,6 +2306,7 @@ 'server string' => 'Server String', 'service' => 'Service', 'service added' => 'Custom network service added', +'service boot setting unavailable' => 'No valid runlevel symlink was found for the initscript of this service.', 'service name' => 'Service name:', 'service removed' => 'Custom network service removed', 'service updated' => 'Custom network service updated', diff --git a/langs/nl/cgi-bin/nl.pl b/langs/nl/cgi-bin/nl.pl index 49dabec99..4fd6955cc 100644 --- a/langs/nl/cgi-bin/nl.pl +++ b/langs/nl/cgi-bin/nl.pl @@ -1899,6 +1899,7 @@ 'server string' => 'Server String', 'service' => 'Dienst', 'service added' => 'Aangepaste netwerkdienst toegevoegd', +'service boot setting unavailable' => 'Er werd voor het initscript van deze service geen geldige runlevel symlink gevonden.', 'service name' => 'Dienstennaam:', 'service removed' => 'Aangepaste netwerkdienst verwijderd', 'service updated' => 'Aangepaste netwerkdienst bijgewerkt',