diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..b3c89efc85 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd). + + diff --git a/dockers/docker-sonic-telemetry/telemetry.sh b/dockers/docker-sonic-telemetry/telemetry.sh index c7693adc16..acd13762d9 100755 --- a/dockers/docker-sonic-telemetry/telemetry.sh +++ b/dockers/docker-sonic-telemetry/telemetry.sh @@ -33,6 +33,9 @@ if [ -n "$CERTS" ]; then if [ ! -z $CA_CRT ]; then TELEMETRY_ARGS+=" --ca_crt $CA_CRT" fi + + # Reuse GNMI_CLIENT_CERT for telemetry service + TELEMETRY_ARGS+=" --config_table_name GNMI_CLIENT_CERT" elif [ -n "$X509" ]; then SERVER_CRT=$(echo $X509 | jq -r '.server_crt') SERVER_KEY=$(echo $X509 | jq -r '.server_key') diff --git a/platform/broadcom/platform-modules-nokia.mk b/platform/broadcom/platform-modules-nokia.mk index fb5199375e..01a7ca60d0 100644 --- a/platform/broadcom/platform-modules-nokia.mk +++ b/platform/broadcom/platform-modules-nokia.mk @@ -12,3 +12,7 @@ $(NOKIA_IXR7250_PLATFORM_MODULE)_PLATFORM = x86_64-nokia_ixr7250e_sup-r0 $(NOKIA_IXR7250_PLATFORM_MODULE)_PLATFORM += x86_64-nokia_ixr7250e_36x400g-r0 SONIC_DPKG_DEBS += $(NOKIA_IXR7250_PLATFORM_MODULE) +NDK_VERSION = 22.9.23 +NOKIA_NDK_DEBIAN = ndk_$(NDK_VERSION)_amd64.deb +$(NOKIA_NDK_DEBIAN)_URL = "https://github.com/Nokia-ION/ndk-releases/releases/download/v$(NDK_VERSION)/$(NOKIA_NDK_DEBIAN)" +SONIC_ONLINE_DEBS += $(NOKIA_NDK_DEBIAN) diff --git a/src/iccpd/src/mlacp_link_handler.c b/src/iccpd/src/mlacp_link_handler.c index c0e561988e..b3e826d2b0 100644 --- a/src/iccpd/src/mlacp_link_handler.c +++ b/src/iccpd/src/mlacp_link_handler.c @@ -258,39 +258,6 @@ static int ndisc_set_handler(struct CSM *csm, struct LocalInterface *lif, int ad * Port-Channel Status Handler * ****************************************/ -static void set_route_by_linux_route(struct CSM* csm, - struct LocalInterface *local_if, - int is_add) -{ - /* TODO Need to remove this function - when set static route with zebra works fine*/ - - char ipv4_dest_str[INET_ADDRSTRLEN]; - char syscmd[128]; - char *ptr; - int ret = 0; - - /* enable kernel forwarding support*/ - system("echo 1 > /proc/sys/net/ipv4/ip_forward"); - - if (!csm || !local_if) - return; - - sprintf(ipv4_dest_str, "%s", show_ip_str(htonl(local_if->ipv4_addr))); - ptr = strrchr(ipv4_dest_str, '.'); - strcpy(ptr, ".0\0"); - - /* set gw route */ - /* sprintf(syscmd, "ip route %s %s/%d proto static metric 200 nexthop via %s > /dev/null 2>&1", */ - sprintf(syscmd, "ip route %s %s/%d metric 200 nexthop via %s > /dev/null 2>&1", - (is_add) ? "add" : "del", ipv4_dest_str, local_if->prefixlen, csm->peer_ip); - - ret = system(syscmd); - ICCPD_LOG_DEBUG(__FUNCTION__, "%s ret = %d", syscmd, ret); - - return; -} - static void update_vlan_if_info(struct CSM *csm, struct LocalInterface *local_if, struct LocalInterface *vlan_if, @@ -354,7 +321,6 @@ static void set_l3_itf_state(struct CSM *csm, /* set static route*/ if (route_type == ROUTE_ADD) { - /*set_route_by_linux_route(csm, set_l3_local_if, 1);*/ /*add static route by linux route tool*/ /*If the L3 intf is not Vlan, del ARP; else wait ARP age*/ if (strncmp(set_l3_local_if->name, VLAN_PREFIX, 4) != 0) { @@ -364,7 +330,6 @@ static void set_l3_itf_state(struct CSM *csm, } else if (route_type == ROUTE_DEL) { - /*set_route_by_linux_route(csm, set_l3_local_if, 0);*/ /*del static route by linux route tool*/ arp_set_handler(csm, set_l3_local_if, 1); /* add arp*/ ndisc_set_handler(csm, set_l3_local_if, 1); /* add nd */ } diff --git a/src/radius/nss/libnss-radius/nss_radius_common.c b/src/radius/nss/libnss-radius/nss_radius_common.c index 652d04ae5c..bd828ffc80 100644 --- a/src/radius/nss/libnss-radius/nss_radius_common.c +++ b/src/radius/nss/libnss-radius/nss_radius_common.c @@ -25,6 +25,7 @@ The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. #include #include #include +#include #include "nss_radius_common.h" @@ -167,6 +168,124 @@ static void init_rnm(RADIUS_NSS_CONF_B * conf) { } +static int user_add(const char* name, char* gid, char* sec_grp, char* gecos, + char* home, char* shell, const char* unconfirmed_user, int many_to_one) { + pid_t pid, w; + int status = 0; + int wstatus; + char cmd[64]; + + snprintf(cmd, 63, "%s", USERADD); + + pid = fork(); + + if(pid > 0) { + do { + w = waitpid(pid, &wstatus, WUNTRACED | WCONTINUED); + if (w == -1) + return -1; + } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus)); + if WIFEXITED(wstatus) + return WEXITSTATUS(wstatus); + else + return -1; + + // Child + + } else if(pid == 0) { + + if (many_to_one) + execl(cmd, cmd, "-g", gid, "-G", sec_grp, "-c", gecos, "-m", "-s", shell, name, NULL); + else + execl(cmd, cmd, "-U", "-G", sec_grp, "-c", unconfirmed_user, "-d", home, "-m", "-s", shell, name, NULL); + syslog(LOG_ERR, "exec of %s failed with errno=%d", cmd, errno); + return -1; + + // Error + } else { + fprintf(stderr, "error forking the child\n"); + return -1; + } + + return status; +} + +static int user_del(const char* name) { + pid_t pid, w; + int status = 0; + int wstatus; + char cmd[64]; + + snprintf(cmd, 63, "%s", USERDEL); + + pid = fork(); + + if(pid > 0) { + do { + w = waitpid(pid, &wstatus, WUNTRACED | WCONTINUED); + if (w == -1) + return -1; + } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus)); + if WIFEXITED(wstatus) + return WEXITSTATUS(wstatus); + else + return -1; + + // Child + + } else if(pid == 0) { + + execl(cmd, cmd, "-r", name, NULL); + syslog(LOG_ERR, "exec of %s failed with errno=%d", cmd, errno); + return -1; + + // Error + } else { + fprintf(stderr, "error forking the child\n"); + return -1; + } + + return status; +} + +static int user_mod(const char* name, char* sec_grp) { + pid_t pid, w; + int status = 0; + int wstatus; + char cmd[64]; + + snprintf(cmd, 63, "%s", USERMOD); + + pid = fork(); + + if(pid > 0) { + do { + w = waitpid(pid, &wstatus, WUNTRACED | WCONTINUED); + if (w == -1) + return -1; + } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus)); + if WIFEXITED(wstatus) + return WEXITSTATUS(wstatus); + else + return -1; + + // Child + + } else if(pid == 0) { + + execl(cmd, cmd, "-G", sec_grp, "-c", name, name, NULL); + syslog(LOG_ERR, "exec of %s failed with errno=%d", cmd, errno); + return -1; + + // Error + } else { + fprintf(stderr, "error forking the child\n"); + return -1; + } + + return status; +} + int parse_nss_config(RADIUS_NSS_CONF_B * conf, char * prog, char * file_buf, int file_buf_sz, int * errnop, int * plockfd) { @@ -379,22 +498,6 @@ int unparse_nss_config(RADIUS_NSS_CONF_B * conf, int * errnop, int * plockfd) { return 0; } -static int invoke_popen(RADIUS_NSS_CONF_B * conf, char * cmd) { - FILE * fp; - int status = 0; - - if (conf->debug) - syslog(LOG_DEBUG, "%s:%s", conf->prog, cmd); - - if (((fp = popen(cmd, "r")) == NULL) || (pclose(fp) == -1)) { - syslog(LOG_ERR, "%s: %s: popen()/pclose() failed %p, errno=%d", - conf->prog, cmd, fp, errno); - status = errno; - } - - return status; -} - static int radius_getpwnam_r_cleanup(int status, FILE * fp) { if (fp) fclose(fp); @@ -434,10 +537,8 @@ static int radius_update_user_cleanup(int status) { int radius_update_user(RADIUS_NSS_CONF_B * conf, const char * user, int mpl) { char buf[BUFLEN]; - char usermod[4096]; struct passwd pw, *result = NULL; RADIUS_NSS_MPL * rnm = NULL; - int written = 0; int status; /* Verify uid is not in the reserved range (<=1000). @@ -466,82 +567,53 @@ int radius_update_user(RADIUS_NSS_CONF_B * conf, const char * user, int mpl) { if (conf->trace) dump_rnm(mpl, rnm, "update"); - written = snprintf(usermod, sizeof(usermod), - "%s -G %s -c \"%s\" \"%s\"", USERMOD, rnm->groups, user, user); - - if (written >= sizeof(usermod)) { - syslog(LOG_ERR, - "%s: truncated usermod cmd. Skipping:\"%s\"\n", conf->prog, usermod); - return radius_update_user_cleanup(STATUS_E2BIG); + if(0 != user_mod(user, rnm->groups)) { + syslog(LOG_ERR, "%s: %s %s failed", conf->prog, USERMOD, user); + return -1; } - - return radius_update_user_cleanup(invoke_popen(conf, usermod)); -} - -static int radius_create_user_cleanup(int status) { - return status; + return 0; } int radius_create_user(RADIUS_NSS_CONF_B * conf, const char * user, int mpl, int unconfirmed) { - char buf[BUFLEN]; - char useradd[4096]; + char buf[BUFLEN] = {0}; RADIUS_NSS_MPL * rnm = &((conf->rnm)[mpl-1]); - int written = 0; if (conf->trace) dump_rnm(mpl, rnm, "create"); + if(strlen(user) > 32) { + syslog(LOG_ERR, "%s: Username too long", conf->prog); + return -1; + } - if (conf->many_to_one) { + syslog(LOG_INFO, "%s: Creating user \"%s\"", conf->prog, user); - written = snprintf(useradd, sizeof(useradd), - "%s -g %d -G %s -c \"%s\" -m -s %s \"%s\"", - USERADD, rnm->gid, rnm->groups, rnm->gecos, rnm->shell, user); + char sgid[10] = {0}; + char home[64] = {0}; + snprintf(sgid, 10, "%d", rnm->gid); + snprintf(home, 63, "/home/%s", user); - } else { + snprintf(buf, sizeof(buf), "Unconfirmed-%ld", time(NULL)); - snprintf(buf, sizeof(buf), "Unconfirmed-%ld", time(NULL)); - written = snprintf(useradd, sizeof(useradd), - "%s -U -G %s -c \"%s\" -d \"/home/%s\" -m -s %s \"%s\"", - USERADD, rnm->groups, unconfirmed ? buf : user, user, - rnm->shell, user); - - } + if(0 != user_add(user, sgid, rnm->groups, rnm->gecos, home, rnm->shell, unconfirmed ? buf : user, conf->many_to_one)) { + syslog(LOG_ERR, "%s: %s %s failed", conf->prog, USERADD, user); - if (written >= sizeof(useradd)) { - syslog(LOG_ERR, - "%s: truncated useradd cmd. Skipping:\"%s\"\n", conf->prog, useradd); - return radius_create_user_cleanup(STATUS_E2BIG); + return -1; } - - syslog(LOG_INFO, "%s: Creating user \"%s\"", conf->prog, user); - - return radius_create_user_cleanup(invoke_popen(conf, useradd)); -} - -static int radius_delete_user_cleanup(int status) { - return status; + return 0; } int radius_delete_user(RADIUS_NSS_CONF_B * conf, const char * user) { - char buf[BUFLEN]; - char userdel[4096]; - int written = 0; - - written = snprintf(userdel, sizeof(userdel), "%s -r \"%s\"", USERDEL, user); - - if (written >= sizeof(userdel)) { - syslog(LOG_ERR, - "%s: truncated userdel cmd. Skipping:\"%s\"\n", conf->prog, userdel); - return radius_delete_user_cleanup(STATUS_E2BIG); - } - syslog(LOG_INFO, "%s: Deleting user \"%s\"", conf->prog, user); + if(0 != user_del(user)) { + syslog(LOG_ERR, "%s: %s %s failed", conf->prog, USERDEL, user); - return radius_delete_user_cleanup(invoke_popen(conf, userdel)); + return -1; + } + return 0; } int radius_clear_unconfirmed_users_cleanup(int status, FILE * fp) { diff --git a/src/sonic-yang-models/tests/files/sample_config_db.json b/src/sonic-yang-models/tests/files/sample_config_db.json index 1751582b83..d35d40b30d 100644 --- a/src/sonic-yang-models/tests/files/sample_config_db.json +++ b/src/sonic-yang-models/tests/files/sample_config_db.json @@ -1220,6 +1220,14 @@ "port": "50052" } }, + "GNMI_CLIENT_CERT": { + "testcert1": { + "role": "RW" + }, + "testcert2": { + "role": "RO" + } + }, "TUNNEL": { "MuxTunnel0": { "dscp_mode": "uniform", diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/gnmi.json b/src/sonic-yang-models/tests/yang_model_tests/tests/gnmi.json index 5938290f8a..10956d2bbf 100644 --- a/src/sonic-yang-models/tests/yang_model_tests/tests/gnmi.json +++ b/src/sonic-yang-models/tests/yang_model_tests/tests/gnmi.json @@ -13,5 +13,12 @@ }, "GNMI_TABLE_WITH_VALID_CONFIG": { "desc": "TABLE WITH VALID CONFIG." + }, + "GNMI_CLIENT_CERT_LIST_TABLE_WITH_MISSING_ROLE": { + "desc": "CLIENT_CERT_LIST_TABLE_WITH_MISSING_ROLE failure.", + "eStrKey": "Mandatory" + }, + "GNMI_CLIENT_CERT_LIST_TABLE_WITH_VALID_CONFIG": { + "desc": "TABLE WITH VALID CONFIG." } } diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests_config/gnmi.json b/src/sonic-yang-models/tests/yang_model_tests/tests_config/gnmi.json index db121ae394..ea83bc90d0 100644 --- a/src/sonic-yang-models/tests/yang_model_tests/tests_config/gnmi.json +++ b/src/sonic-yang-models/tests/yang_model_tests/tests_config/gnmi.json @@ -62,5 +62,32 @@ } } } + }, + "GNMI_CLIENT_CERT_LIST_TABLE_WITH_MISSING_ROLE": { + "sonic-gnmi:sonic-gnmi": { + "sonic-gnmi:GNMI_CLIENT_CERT": { + "GNMI_CLIENT_CERT_LIST": [ + { + "cert_cname": "testcert1" + } + ] + } + } + }, + "GNMI_CLIENT_CERT_LIST_TABLE_WITH_VALID_CONFIG": { + "sonic-gnmi:sonic-gnmi": { + "sonic-gnmi:GNMI_CLIENT_CERT": { + "GNMI_CLIENT_CERT_LIST": [ + { + "cert_cname": "testcert1", + "role": "RW" + }, + { + "cert_cname": "testcert2", + "role": "RO" + } + ] + } + } } } diff --git a/src/sonic-yang-models/yang-models/sonic-gnmi.yang b/src/sonic-yang-models/yang-models/sonic-gnmi.yang index 1d6b228266..b27ab84938 100644 --- a/src/sonic-yang-models/yang-models/sonic-gnmi.yang +++ b/src/sonic-yang-models/yang-models/sonic-gnmi.yang @@ -72,7 +72,28 @@ module sonic-gnmi { } } + } + + container GNMI_CLIENT_CERT { + description "GNMI client cert list"; + list GNMI_CLIENT_CERT_LIST { + max-elements 8; + key "cert_cname"; + + leaf cert_cname { + type string; + description + "client cert common name"; + } + + leaf role { + type string; + mandatory true; + description + "role of client cert common name"; + } + } } } } diff --git a/src/systemd-sonic-generator/Makefile b/src/systemd-sonic-generator/Makefile index 0e6fb9095d..4fa628c249 100644 --- a/src/systemd-sonic-generator/Makefile +++ b/src/systemd-sonic-generator/Makefile @@ -2,15 +2,15 @@ CC=gcc CFLAGS += -std=gnu99 -D_GNU_SOURCE CXX=g++ -CXXFLAGS += -std=c++11 -D_GNU_SOURCE -LDFLAGS += -lpthread -lboost_filesystem -lboost_system -lgtest +CXXFLAGS += -std=c++11 -D_GNU_SOURCE -I ./ +LDFLAGS += -l stdc++ -lpthread -lboost_filesystem -lboost_system -lgtest BINARY = systemd-sonic-generator -$(BINARY): systemd-sonic-generator.c +$(BINARY): systemd-sonic-generator.cpp rm -f ./systemd-sonic-generator - $(CC) $(CFLAGS) -o $@ $^ + $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) install: $(BINARY) mkdir -p $(DESTDIR) @@ -24,10 +24,12 @@ test: ssg_test ./ssg_test ssg_test: ssg-test.cc systemd-sonic-generator.o - $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) + $(CXX) $(CXXFLAGS) -ggdb -o $@ $^ $(LDFLAGS) + +systemd-sonic-generator.o: systemd-sonic-generator.cpp + $(CXX) $(CXXFLAGS) -ggdb -D_SSG_UNITTEST -o $@ -c $^ -systemd-sonic-generator.o: systemd-sonic-generator.c - $(CC) $(CFLAGS) -D_SSG_UNITTEST -o $@ -c $^ +all: $(BINARY) test clean: rm -f ./systemd-sonic-generator diff --git a/src/systemd-sonic-generator/ssg-test.cc b/src/systemd-sonic-generator/ssg-test.cc index 1d0f33d54b..567da24ec0 100644 --- a/src/systemd-sonic-generator/ssg-test.cc +++ b/src/systemd-sonic-generator/ssg-test.cc @@ -181,8 +181,7 @@ class SsgMainTest : public SsgFunctionTest { /* Find a string in a file */ bool find_string_in_file(std::string str, - std::string file_name, - int num_asics) { + std::string file_name) { bool found = false; std::string line; @@ -214,7 +213,7 @@ class SsgMainTest : public SsgFunctionTest { /* Run once for single instance */ finished = true; } - EXPECT_EQ(find_string_in_file(str_t, target, num_asics), + EXPECT_EQ(find_string_in_file(str_t, target), expected_result) << "Error validating " + str_t + " in " + target; } @@ -460,9 +459,8 @@ TEST_F(SsgFunctionTest, insert_instance_number) { char input[] = "test@.service"; for (int i = 0; i <= 100; ++i) { std::string out = "test@" + std::to_string(i) + ".service"; - char* ret = insert_instance_number(input, i); - ASSERT_NE(ret, nullptr); - EXPECT_STREQ(ret, out.c_str()); + std::string ret = insert_instance_number(input, i); + EXPECT_EQ(ret, out); } } @@ -513,7 +511,6 @@ TEST_F(SsgFunctionTest, get_unit_files) { /* TEST ssg_main() argv error */ TEST_F(SsgMainTest, ssg_main_argv) { - FILE* fp; std::vector argv_; std::vector arguments = { "ssg_main", diff --git a/src/systemd-sonic-generator/systemd-sonic-generator.c b/src/systemd-sonic-generator/systemd-sonic-generator.cpp similarity index 69% rename from src/systemd-sonic-generator/systemd-sonic-generator.c rename to src/systemd-sonic-generator/systemd-sonic-generator.cpp index 74f08fe396..d0359a4a1d 100644 --- a/src/systemd-sonic-generator/systemd-sonic-generator.c +++ b/src/systemd-sonic-generator/systemd-sonic-generator.cpp @@ -1,13 +1,15 @@ -#include #include #include #include #include #include -#include +// #include #include #include #include +#include +#include +#include #define MAX_NUM_TARGETS 48 #define MAX_NUM_INSTALL_LINES 48 @@ -48,13 +50,25 @@ void strip_trailing_newline(char* str) { Strips trailing newline from a string if it exists ***/ + if (str == NULL) { + return; + } size_t l = strlen(str); if (l > 0 && str[l-1] == '\n') str[l-1] = '\0'; } +void strip_trailing_newline(std::string& str) { + /*** + Strips trailing newline from a string if it exists + ***/ + if (!str.empty() && str.back() == '\n') { + str.pop_back(); + } +} + -static int get_target_lines(char* unit_file, char* target_lines[]) { +static int get_target_lines(const char* unit_file, char* target_lines[]) { /*** Gets installation information for a given unit file @@ -101,26 +115,32 @@ static int get_target_lines(char* unit_file, char* target_lines[]) { return num_target_lines; } -static bool is_multi_instance_service(char *service_name){ - int i; - for(i=0; i < num_multi_inst; i++){ - /* - * The service name may contain @.service or .service. Remove these - * postfixes and extract service name. Compare service name for absolute - * match in multi_instance_services[]. - * This is to prevent services like database-chassis and systemd-timesyncd marked - * as multi instance services as they contain strings 'database' and 'syncd' respectively - * which are multi instance services in multi_instance_services[]. - */ - char *saveptr; - char *token = strtok_r(service_name, "@", &saveptr); - if (token) { - if (strstr(token, ".service") != NULL) { - /* If we are here, service_name did not have '@' delimiter but contains '.service' */ - token = strtok_r(service_name, ".", &saveptr); +static bool is_multi_instance_service(std::string service_file, std::unordered_set service_list=std::unordered_set()){ + /* + * The service name may contain @.service or .service. Remove these + * postfixes and extract service name. Compare service name for absolute + * match in multi_instance_services[]. + * This is to prevent services like database-chassis and systemd-timesyncd marked + * as multi instance services as they contain strings 'database' and 'syncd' respectively + * which are multi instance services in multi_instance_services[]. + */ + std::string delimiter; + if (service_file.find("@") != std::string::npos) { + delimiter = "@"; + } else { + delimiter = "."; + } + std::string service_name = service_file.substr(0, service_file.find(delimiter)); + + if (service_list.empty()) { + for(int i=0; i < num_multi_inst; i++){ + + if (service_name == multi_instance_services[i]) { + return true; } } - if (strncmp(service_name, multi_instance_services[i], strlen(service_name)) == 0) { + } else { + if (service_list.count(service_name) > 0) { return true; } } @@ -128,59 +148,49 @@ static bool is_multi_instance_service(char *service_name){ } -static int get_install_targets_from_line(char* target_string, char* install_type, char* targets[], int existing_targets) { +static int get_install_targets_from_line(std::string target_string, std::string install_type, char* targets[], int existing_targets) { /*** Helper fuction for get_install_targets Given a space delimited string of target directories and a suffix, puts each target directory plus the suffix into the targets array ***/ - char* token; - char* target; - char* saveptr; - char final_target[PATH_MAX]; + std::string target; int num_targets = 0; - while ((token = strtok_r(target_string, " ", &target_string))) { - if (num_targets + existing_targets >= MAX_NUM_TARGETS) { - fputs("Number of targets found exceeds MAX_NUM_TARGETS\n", stderr); - fputs("Additional targets will be ignored \n", stderr); - return num_targets; - } - - target = strdup(token); - strip_trailing_newline(target); + if (target_string.empty() || install_type.empty()) { + fprintf(stderr, "Invalid target string or install type\n"); + exit(EXIT_FAILURE); + } - if (strstr(target, "%") != NULL) { - char* prefix = strtok_r(target, ".", &saveptr); - char* suffix = strtok_r(NULL, ".", &saveptr); - int prefix_len = strlen(prefix); + std::stringstream ss(target_string); - strncpy(final_target, prefix, prefix_len - 2); - final_target[prefix_len - 2] = '\0'; - strcat(final_target, "."); - strcat(final_target, suffix); + while (ss >> target) { + if (num_targets + existing_targets >= MAX_NUM_TARGETS) { + fprintf(stderr, "Number of targets exceeds MAX_NUM_TARGETS\n"); + fputs("Additional targets will be ignored\n", stderr); + break; } - else { - strcpy(final_target, target); + // handle install targets using the '%i' systemd specifier + if (target.find("%") != std::string::npos) { + target = target.substr(0, target.find("%")) + target.substr(target.find(".")); } - strcat(final_target, install_type); - - free(target); - - targets[num_targets + existing_targets] = strdup(final_target); + strip_trailing_newline(target); + target += install_type; + targets[num_targets + existing_targets] = (char*) calloc(target.length() + 1, sizeof(char)); + snprintf(targets[num_targets + existing_targets], PATH_MAX, "%s", target.c_str()); num_targets++; } return num_targets; } -static void replace_multi_inst_dep(char *src) { +static void replace_multi_inst_dep(const char *src) { FILE *fp_src; FILE *fp_tmp; char buf[MAX_BUF_SIZE]; char* line = NULL; int i; - ssize_t len; + size_t len; char *token; char *word; char *line_copy; @@ -254,14 +264,14 @@ static void replace_multi_inst_dep(char *src) { rename(tmp_file_path, src); } -int get_install_targets(char* unit_file, char* targets[]) { +int get_install_targets(std::string unit_file, char* targets[]) { /*** Returns install targets for a unit file Parses the information in the [Install] section of a given unit file to determine which directories to install the unit in ***/ - char file_path[PATH_MAX]; + std::string file_path; char *target_lines[MAX_NUM_INSTALL_LINES]; int num_target_lines; int num_targets; @@ -269,25 +279,20 @@ int get_install_targets(char* unit_file, char* targets[]) { char* token; char* line = NULL; bool first; - char* target_suffix; - char *instance_name; - char *dot_ptr; + std::string target_suffix; + std::string instance_name; - strcpy(file_path, get_unit_file_prefix()); - strcat(file_path, unit_file); + file_path = get_unit_file_prefix() + unit_file; - instance_name = strdup(unit_file); - dot_ptr = strchr(instance_name, '.'); - *dot_ptr = '\0'; + instance_name = unit_file.substr(0, unit_file.find('.')); if((num_asics > 1) && (!is_multi_instance_service(instance_name))) { - replace_multi_inst_dep(file_path); + replace_multi_inst_dep(file_path.c_str()); } - free(instance_name); - num_target_lines = get_target_lines(file_path, target_lines); + num_target_lines = get_target_lines(file_path.c_str(), target_lines); if (num_target_lines < 0) { - fprintf(stderr, "Error parsing targets for %s\n", unit_file); + fprintf(stderr, "Error parsing targets for %s\n", unit_file.c_str()); return -1; } @@ -340,7 +345,7 @@ int get_unit_files(char* unit_files[]) { int num_unit_files = 0; num_multi_inst = 0; - multi_instance_services = calloc(MAX_NUM_UNITS, sizeof(char *)); + multi_instance_services = (char**) calloc(MAX_NUM_UNITS, sizeof(char *)); while ((read = getline(&line, &len, fp)) != -1) { if (num_unit_files >= MAX_NUM_UNITS) { @@ -352,8 +357,8 @@ int get_unit_files(char* unit_files[]) { /* Get the multi-instance services */ pos = strchr(line, '@'); if (pos != NULL) { - multi_instance_services[num_multi_inst] = calloc(strlen(line), sizeof(char)); - strncpy(multi_instance_services[num_multi_inst], line, pos-line); + multi_instance_services[num_multi_inst] = (char*) calloc(pos-line+1, sizeof(char)); + snprintf(multi_instance_services[num_multi_inst], pos-line+1, "%s", line); num_multi_inst++; } @@ -374,100 +379,80 @@ int get_unit_files(char* unit_files[]) { } -char* insert_instance_number(char* unit_file, int instance) { +std::string insert_instance_number(const std::string& unit_file, int instance) { /*** Adds an instance number to a systemd template name E.g. given unit_file='example@.service', instance=3, returns a pointer to 'example@3.service' ***/ - char* instance_name; - int ret; - int prefix_len; - const char *suffix = strchr(unit_file, '@'); - if (!suffix) { - fprintf(stderr, "Invalid unit file %s for instance %d\n", unit_file, instance); - return NULL; + size_t at_pos = unit_file.find("@"); + if (at_pos == std::string::npos) { + fprintf(stderr, "Invalid unit file %s for instance %d\n", unit_file.c_str(), instance); + return ""; } - /*** - suffix is "@.service", set suffix=".service" - prefix_len is length of "example@" - ***/ - prefix_len = ++suffix - unit_file; - ret = asprintf(&instance_name, "%.*s%d%s", prefix_len, unit_file, instance, suffix); - if (ret == -1) { - fprintf(stderr, "Error creating instance %d of %s\n", instance, unit_file); - return NULL; - } - - return instance_name; + return unit_file.substr(0, at_pos + 1) + std::to_string(instance) + unit_file.substr(at_pos + 1); } -static int create_symlink(char* unit, char* target, char* install_dir, int instance) { +static int create_symlink(const std::string& unit, const std::string& target, const std::string& install_dir, int instance) { struct stat st; - char src_path[PATH_MAX]; - char dest_path[PATH_MAX]; - char final_install_dir[PATH_MAX]; - char* unit_instance; + std::string src_path; + std::string dest_path; + std::string final_install_dir; + std::string unit_instance; int r; - strcpy(src_path, get_unit_file_prefix()); - strcat(src_path, unit); + src_path = get_unit_file_prefix() + unit; if (instance < 0) { - unit_instance = strdup(unit); + unit_instance = unit; } else { unit_instance = insert_instance_number(unit, instance); } - strcpy(final_install_dir, install_dir); - strcat(final_install_dir, target); - strcpy(dest_path, final_install_dir); - strcat(dest_path, "/"); - strcat(dest_path, unit_instance); - - free(unit_instance); + final_install_dir = install_dir + std::string(target); + dest_path = final_install_dir + "/" + unit_instance; - if (stat(final_install_dir, &st) == -1) { + if (stat(final_install_dir.c_str(), &st) == -1) { // If doesn't exist, create - r = mkdir(final_install_dir, 0755); + r = mkdir(final_install_dir.c_str(), 0755); if (r == -1) { - fprintf(stderr, "Unable to create target directory %s\n", final_install_dir); + fprintf(stderr, "Unable to create target directory %s\n", final_install_dir.c_str()); return -1; } } else if (S_ISREG(st.st_mode)) { // If is regular file, remove and create - r = remove(final_install_dir); + r = remove(final_install_dir.c_str()); if (r == -1) { - fprintf(stderr, "Unable to remove file with same name as target directory %s\n", final_install_dir); + fprintf(stderr, "Unable to remove file with same name as target directory %s\n", final_install_dir.c_str()); return -1; } - r = mkdir(final_install_dir, 0755); + r = mkdir(final_install_dir.c_str(), 0755); if (r == -1) { - fprintf(stderr, "Unable to create target directory %s\n", final_install_dir); + fprintf(stderr, "Unable to create target directory %s\n", final_install_dir.c_str()); return -1; } } else if (S_ISDIR(st.st_mode)) { // If directory, verify correct permissions - r = chmod(final_install_dir, 0755); + r = chmod(final_install_dir.c_str(), 0755); if (r == -1) { - fprintf(stderr, "Unable to change permissions of existing target directory %s\n", final_install_dir); + fprintf(stderr, "Unable to change permissions of existing target directory %s\n", final_install_dir.c_str()); return -1; } } - r = symlink(src_path, dest_path); + r = symlink(src_path.c_str(), dest_path.c_str()); if (r < 0) { if (errno == EEXIST) return 0; - fprintf(stderr, "Error creating symlink %s from source %s\n", dest_path, src_path); + fprintf(stderr, "Error creating symlink %s from source %s\n", dest_path.c_str(), src_path.c_str()); return -1; } @@ -476,7 +461,7 @@ static int create_symlink(char* unit, char* target, char* install_dir, int insta } -static int install_unit_file(char* unit_file, char* target, char* install_dir) { +static int install_unit_file(std::string unit_file, std::string target, std::string install_dir) { /*** Creates a symlink for a unit file installation @@ -487,38 +472,34 @@ static int install_unit_file(char* unit_file, char* target, char* install_dir) { If a multi ASIC platform is detected, enables multi-instance services as well ***/ - char* target_instance; - char* prefix; - char* suffix; + std::string target_instance; int r; - assert(unit_file); - assert(target); - + if (unit_file.empty() || target.empty() || install_dir.empty()){ + fprintf(stderr, "Invalid unit file, target or install directory\n"); + exit(EXIT_FAILURE); + } - if ((num_asics > 1) && strstr(unit_file, "@") != NULL) { + if ((num_asics > 1) && unit_file.find("@") != std::string::npos) { for (int i = 0; i < num_asics; i++) { - if (strstr(target, "@") != NULL) { + if (target.find("@") != std::string::npos) { target_instance = insert_instance_number(target, i); } else { - target_instance = strdup(target); + target_instance = target; } r = create_symlink(unit_file, target_instance, install_dir, i); if (r < 0) - fprintf(stderr, "Error installing %s for target %s\n", unit_file, target_instance); - - free(target_instance); - + fprintf(stderr, "Error installing %s for target %s\n", unit_file.c_str(), target_instance.c_str()); } } else { r = create_symlink(unit_file, target, install_dir, -1); if (r < 0) - fprintf(stderr, "Error installing %s for target %s\n", unit_file, target); + fprintf(stderr, "Error installing %s for target %s\n", unit_file.c_str(), target.c_str()); } return 0; @@ -531,12 +512,10 @@ int get_num_of_asic() { ***/ FILE *fp; char *line = NULL; - char* token; char* platform = NULL; char* saveptr; size_t len = 0; ssize_t nread; - bool ans; char asic_file[512]; char* str_num_asic; int num_asic = 1; @@ -552,7 +531,7 @@ int get_num_of_asic() { while ((nread = getline(&line, &len, fp)) != -1) { if ((strstr(line, "onie_platform") != NULL) || (strstr(line, "aboot_platform") != NULL)) { - token = strtok_r(line, "=", &saveptr); + strtok_r(line, "=", &saveptr); platform = strtok_r(NULL, "=", &saveptr); strip_trailing_newline(platform); break; @@ -565,7 +544,7 @@ int get_num_of_asic() { if (fp != NULL) { while ((nread = getline(&line, &len, fp)) != -1) { if (strstr(line, "NUM_ASIC") != NULL) { - token = strtok_r(line, "=", &saveptr); + strtok_r(line, "=", &saveptr); str_num_asic = strtok_r(NULL, "=", &saveptr); strip_trailing_newline(str_num_asic); if (str_num_asic != NULL){ @@ -584,15 +563,13 @@ int get_num_of_asic() { int ssg_main(int argc, char **argv) { char* unit_files[MAX_NUM_UNITS]; - char install_dir[PATH_MAX]; + std::string install_dir; char* targets[MAX_NUM_TARGETS]; - char* unit_instance; - char* prefix; - char* suffix; - char* saveptr; + std::string unit_instance; + std::string prefix; + std::string suffix; int num_unit_files; int num_targets; - int r; if (argc <= 1) { fputs("Installation directory required as argument\n", stderr); @@ -600,40 +577,33 @@ int ssg_main(int argc, char **argv) { } num_asics = get_num_of_asic(); - strcpy(install_dir, argv[1]); - strcat(install_dir, "/"); + install_dir = std::string(argv[1]) + "/"; num_unit_files = get_unit_files(unit_files); // For each unit file, get the installation targets and install the unit for (int i = 0; i < num_unit_files; i++) { - unit_instance = strdup(unit_files[i]); - if ((num_asics == 1) && strstr(unit_instance, "@") != NULL) { - prefix = strdup(strtok_r(unit_instance, "@", &saveptr)); - suffix = strdup(strtok_r(NULL, "@", &saveptr)); - - strcpy(unit_instance, prefix); - strcat(unit_instance, suffix); + unit_instance = unit_files[i]; + if ((num_asics == 1 && unit_instance.find("@") != std::string::npos)) { + prefix = unit_instance.substr(0, unit_instance.find("@")); + suffix = unit_instance.substr(unit_instance.find("@") + 1); - free(prefix); - free(suffix); + unit_instance = prefix + suffix; } num_targets = get_install_targets(unit_instance, targets); if (num_targets < 0) { - fprintf(stderr, "Error parsing %s\n", unit_instance); - free(unit_instance); + fprintf(stderr, "Error parsing %s\n", unit_instance.c_str()); free(unit_files[i]); continue; } for (int j = 0; j < num_targets; j++) { if (install_unit_file(unit_instance, targets[j], install_dir) != 0) - fprintf(stderr, "Error installing %s to target directory %s\n", unit_instance, targets[j]); + fprintf(stderr, "Error installing %s to target directory %s\n", unit_instance.c_str(), targets[j]); free(targets[j]); } - free(unit_instance); free(unit_files[i]); } diff --git a/src/systemd-sonic-generator/systemd-sonic-generator.h b/src/systemd-sonic-generator/systemd-sonic-generator.h index 25c179caa0..febbda0927 100644 --- a/src/systemd-sonic-generator/systemd-sonic-generator.h +++ b/src/systemd-sonic-generator/systemd-sonic-generator.h @@ -6,9 +6,11 @@ * Copyright (c) 2021 by Cisco Systems, Inc. *------------------------------------------------------------------ */ -#ifdef __cplusplus -extern "C" { -#endif +// #ifdef __cplusplus +// extern "C" { +// #endif +#include +#include /* expose global vars for testing purpose */ extern const char* UNIT_FILE_PREFIX; @@ -25,11 +27,11 @@ extern const char* get_unit_file_prefix(); extern const char* get_config_file(); extern const char* get_machine_config_file(); extern const char* get_asic_conf_format(); -extern char* insert_instance_number(char* unit_file, int instance); +extern std::string insert_instance_number(const std::string& unit_file, int instance); extern int ssg_main(int argc, char** argv); extern int get_num_of_asic(); -extern int get_install_targets(char* unit_file, char* targets[]); +extern int get_install_targets(std::string unit_file, char* targets[]); extern int get_unit_files(char* unit_files[]); -#ifdef __cplusplus -} -#endif +// #ifdef __cplusplus +// } +// #endif