From 3c303e5fe1061b8715be018ae8e535cbc24303c9 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 15 Jul 2009 12:46:06 -0700 Subject: [PATCH] Implement "brctl showmacs" support in brcompat and ovs-brcompatd. This is needed by the Citrix test infrastructure. --- datapath/brcompat.c | 65 +++++ .../compat-2.6/include/linux/netlink.h | 7 +- include/openvswitch/brcompat-netlink.h | 8 + vswitchd/ovs-brcompatd.8.in | 26 +- vswitchd/ovs-brcompatd.c | 257 ++++++++++++++++-- xenserver/etc_init.d_vswitch | 6 +- 6 files changed, 332 insertions(+), 37 deletions(-) diff --git a/datapath/brcompat.c b/datapath/brcompat.c index d945e4e6..db0a70f5 100644 --- a/datapath/brcompat.c +++ b/datapath/brcompat.c @@ -261,6 +261,64 @@ brc_get_port_list(struct net_device *dev, int __user *uindices, int num) return num; } +/* + * Format up to a page worth of forwarding table entries + * userbuf -- where to copy result + * maxnum -- maximum number of entries desired + * (limited to a page for sanity) + * offset -- number of records to skip + */ +static int brc_get_fdb_entries(struct net_device *dev, void __user *userbuf, + unsigned long maxnum, unsigned long offset) +{ + struct nlattr *attrs[BRC_GENL_A_MAX + 1]; + struct sk_buff *request, *reply; + int retval; + int len; + + /* Clamp size to PAGE_SIZE, test maxnum to avoid overflow */ + if (maxnum > PAGE_SIZE/sizeof(struct __fdb_entry)) + maxnum = PAGE_SIZE/sizeof(struct __fdb_entry); + + request = brc_make_request(BRC_GENL_C_FDB_QUERY, dev->name, NULL); + if (!request) + return -ENOMEM; + NLA_PUT_U64(request, BRC_GENL_A_FDB_COUNT, maxnum); + NLA_PUT_U64(request, BRC_GENL_A_FDB_SKIP, offset); + + rtnl_unlock(); + reply = brc_send_command(request, attrs); + retval = PTR_ERR(reply); + if (IS_ERR(reply)) + goto exit; + + retval = -nla_get_u32(attrs[BRC_GENL_A_ERR_CODE]); + if (retval < 0) + goto exit_free_skb; + + retval = -EINVAL; + if (!attrs[BRC_GENL_A_FDB_DATA]) + goto exit_free_skb; + len = nla_len(attrs[BRC_GENL_A_FDB_DATA]); + if (len % sizeof(struct __fdb_entry) || + len / sizeof(struct __fdb_entry) > maxnum) + goto exit_free_skb; + + retval = len / sizeof(struct __fdb_entry); + if (copy_to_user(userbuf, nla_data(attrs[BRC_GENL_A_FDB_DATA]), len)) + retval = -EFAULT; + +exit_free_skb: + kfree_skb(reply); +exit: + rtnl_lock(); + return retval; + +nla_put_failure: + kfree_skb(request); + return -ENOMEM; +} + /* Legacy ioctl's through SIOCDEVPRIVATE. Called with rtnl_lock. */ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) @@ -281,6 +339,10 @@ old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) case BRCTL_GET_PORT_LIST: return brc_get_port_list(dev, (int __user *)args[1], args[2]); + + case BRCTL_GET_FDB_ENTRIES: + return brc_get_fdb_entries(dev, (void __user *)args[1], + args[2], args[3]); } return -EOPNOTSUPP; @@ -357,9 +419,12 @@ static struct genl_ops brc_genl_ops_query_dp = { /* Attribute policy: what each attribute may contain. */ static struct nla_policy brc_genl_policy[BRC_GENL_A_MAX + 1] = { [BRC_GENL_A_ERR_CODE] = { .type = NLA_U32 }, + [BRC_GENL_A_PROC_DIR] = { .type = NLA_NUL_STRING }, [BRC_GENL_A_PROC_NAME] = { .type = NLA_NUL_STRING }, [BRC_GENL_A_PROC_DATA] = { .type = NLA_NUL_STRING }, + + [BRC_GENL_A_FDB_DATA] = { .type = NLA_UNSPEC }, }; static int diff --git a/datapath/linux-2.6/compat-2.6/include/linux/netlink.h b/datapath/linux-2.6/compat-2.6/include/linux/netlink.h index c5f83bd0..fba899ec 100644 --- a/datapath/linux-2.6/compat-2.6/include/linux/netlink.h +++ b/datapath/linux-2.6/compat-2.6/include/linux/netlink.h @@ -17,8 +17,13 @@ static inline struct sk_buff *nlmsg_new_proper(int size, gfp_t flags) { return alloc_skb(size, flags); } - #endif /* linux kernel < 2.6.19 */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) +static inline struct nlmsghdr *nlmsg_hdr(const struct sk_buff *skb) +{ + return (struct nlmsghdr *)skb->data; +} +#endif #endif diff --git a/include/openvswitch/brcompat-netlink.h b/include/openvswitch/brcompat-netlink.h index 09688a95..694bdcc5 100644 --- a/include/openvswitch/brcompat-netlink.h +++ b/include/openvswitch/brcompat-netlink.h @@ -69,6 +69,13 @@ enum { BRC_GENL_A_PROC_NAME, /* U: Name of file in /proc. */ BRC_GENL_A_PROC_DATA, /* U: Contents of file in /proc. */ + /* BRC_GENL_C_FDB_QUERY. */ + BRC_GENL_A_FDB_COUNT, /* K: Number of FDB entries to read. */ + BRC_GENL_A_FDB_SKIP, /* K: Record offset into FDB to start reading. */ + + /* BRC_GENL_C_DP_RESULT. */ + BRC_GENL_A_FDB_DATA, /* U: FDB records. */ + __BRC_GENL_A_MAX, BRC_GENL_A_MAX = __BRC_GENL_A_MAX - 1 }; @@ -88,6 +95,7 @@ enum brc_genl_command { BRC_GENL_C_PORT_DEL, /* K: Port removed from datapath. */ BRC_GENL_C_QUERY_MC, /* U: Get multicast group for brcompat. */ BRC_GENL_C_SET_PROC, /* U: Set contents of file in /proc. */ + BRC_GENL_C_FDB_QUERY, /* K: Read records from forwarding database. */ __BRC_GENL_C_MAX, BRC_GENL_C_MAX = __BRC_GENL_C_MAX - 1 diff --git a/vswitchd/ovs-brcompatd.8.in b/vswitchd/ovs-brcompatd.8.in index ebd67028..68965ca4 100644 --- a/vswitchd/ovs-brcompatd.8.in +++ b/vswitchd/ovs-brcompatd.8.in @@ -16,11 +16,27 @@ that attach to them. It modifies \fIconfig\fR and forces \fBovs\-vswitchd\fR to reload its configuration file. .PP .SH OPTIONS -.IP "\fB--reload-command=\fIcommand\fR" -Sets the command that \fBovs\-brcompatd\fR runs to force \fBovs\-vswitchd\fR to -reload its configuration file to \fIcommand\fR. The command is run in -a subshell, so it may contain arbitrary shell metacharacters, etc. -The \fB--help\fR option displays the default reload command. +.IP "\fB--appctl-command=\fIcommand\fR" +Sets the command that \fBovs\-brcompatd\fR runs to communicate with +\fBovs\-vswitchd\fR. The command is run in \fB/bin/sh\fR as a shell +command, so \fIcommand\fR may contain arbitrary shell metacharacters, +etc. The \fB--help\fR option displays the default command. +.IP +\fIcommand\fR must contain exactly one instance of \fB%s\fR, which +\fBovs\-brcompatd\fR replaces by a command from the set understood by +\fBovs\-vswitchd\fR. Any instances of \fB%%\fR in \fIcommand\fR are +replaced by a single \fB%\fR. The \fB%\fR character may not otherwise +appear in \fIcommand\fR. +.IP +The commands that are substituted into \fIcommand\fR are those that +can be listed by passing \fB-e help\fR to \fBovs\-appctl\fR with +\fBovs\-vswitchd\fR as target. The command that is substituted may +include white space-separated arguments, so \fIcommand\fR should include +shell quotes around \fB%s\fR. +.IP +\fIcommand\fR must not redirect \fBovs\-appctl\fR's standard output or +standard error streams, because \fBovs\-brcompatd\fR expects to read +both of these streams separately. .TP \fB--prune-timeout=\fIsecs\fR . diff --git a/vswitchd/ovs-brcompatd.c b/vswitchd/ovs-brcompatd.c index 9274da0a..16fc7866 100644 --- a/vswitchd/ovs-brcompatd.c +++ b/vswitchd/ovs-brcompatd.c @@ -15,6 +15,7 @@ #include +#include #include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include #include #include @@ -37,6 +39,7 @@ #include "daemon.h" #include "dirs.h" #include "dpif.h" +#include "dynamic-string.h" #include "fatal-signal.h" #include "fault.h" #include "leak-checker.h" @@ -44,6 +47,7 @@ #include "netlink.h" #include "ofpbuf.h" #include "openvswitch/brcompat-netlink.h" +#include "packets.h" #include "poll-loop.h" #include "process.h" #include "signals.h" @@ -82,9 +86,10 @@ static int prune_timeout = 5000; /* Config file shared with ovs-vswitchd (usually ovs-vswitchd.conf). */ static char *config_file; -/* Command to run (via system()) to reload the ovs-vswitchd configuration - * file. */ -static char *reload_command; +/* Shell command to execute (via popen()) to send a control command to the + * running ovs-vswitchd process. The string must contain one instance of %s, + * which is replaced by the control command. */ +static char *appctl_command; /* Netlink socket to listen for interface changes. */ static struct nl_sock *rtnl_sock; @@ -175,6 +180,48 @@ bridge_exists(const char *name) return cfg_has_section("bridge.%s", name); } +static int +execute_appctl_command(const char *unixctl_command, char **output) +{ + char *stdout_log, *stderr_log; + int error, status; + char *argv[5]; + + argv[0] = "/bin/sh"; + argv[1] = "-c"; + argv[2] = xasprintf(appctl_command, unixctl_command); + argv[3] = NULL; + + /* Run process and log status. */ + error = process_run_capture(argv, &stdout_log, &stderr_log, &status); + if (error) { + VLOG_ERR("failed to execute %s command via ovs-appctl: %s", + unixctl_command, strerror(error)); + } else if (status) { + char *msg = process_status_msg(status); + VLOG_ERR("ovs-appctl exited with error (%s)", msg); + free(msg); + error = ECHILD; + } + + /* Deal with stdout_log. */ + if (output) { + *output = stdout_log; + } else { + free(stdout_log); + } + + /* Deal with stderr_log */ + if (stderr_log && *stderr_log) { + VLOG_INFO("ovs-appctl wrote to stderr:\n%s", stderr_log); + } + free(stderr_log); + + free(argv[2]); + + return error; +} + static int rewrite_and_reload_config(void) { @@ -182,20 +229,13 @@ rewrite_and_reload_config(void) int error1 = cfg_write(); int error2 = cfg_read(); long long int reload_start = time_msec(); - int error3 = system(reload_command); + int error3 = execute_appctl_command("vswitchd/reload", NULL); long long int elapsed = time_msec() - reload_start; COVERAGE_INC(brcompatd_reload); if (elapsed > 0) { VLOG_INFO("reload command executed in %lld ms", elapsed); } - if (error3 == -1) { - VLOG_ERR("failed to execute reload command: %s", strerror(errno)); - } else if (error3 != 0) { - char *msg = process_status_msg(error3); - VLOG_ERR("reload command exited with error (%s)", msg); - free(msg); - } - return error1 ? error1 : error2 ? error2 : error3 ? ECHILD : 0; + return error1 ? error1 : error2 ? error2 : error3; } return 0; } @@ -356,17 +396,21 @@ del_bridge(const char *br_name) static int parse_command(struct ofpbuf *buffer, uint32_t *seq, const char **br_name, - const char **port_name) + const char **port_name, uint64_t *count, uint64_t *skip) { static const struct nl_policy policy[] = { [BRC_GENL_A_DP_NAME] = { .type = NL_A_STRING }, [BRC_GENL_A_PORT_NAME] = { .type = NL_A_STRING, .optional = true }, + [BRC_GENL_A_FDB_COUNT] = { .type = NL_A_U64, .optional = true }, + [BRC_GENL_A_FDB_SKIP] = { .type = NL_A_U64, .optional = true }, }; struct nlattr *attrs[ARRAY_SIZE(policy)]; if (!nl_policy_parse(buffer, NLMSG_HDRLEN + GENL_HDRLEN, policy, attrs, ARRAY_SIZE(policy)) - || (port_name && !attrs[BRC_GENL_A_PORT_NAME])) { + || (port_name && !attrs[BRC_GENL_A_PORT_NAME]) + || (count && !attrs[BRC_GENL_A_FDB_COUNT]) + || (skip && !attrs[BRC_GENL_A_FDB_SKIP])) { return EINVAL; } @@ -375,11 +419,17 @@ parse_command(struct ofpbuf *buffer, uint32_t *seq, const char **br_name, if (port_name) { *port_name = nl_attr_get_string(attrs[BRC_GENL_A_PORT_NAME]); } + if (count) { + *count = nl_attr_get_u64(attrs[BRC_GENL_A_FDB_COUNT]); + } + if (skip) { + *skip = nl_attr_get_u64(attrs[BRC_GENL_A_FDB_SKIP]); + } return 0; } static void -send_reply(uint32_t seq, int error) +send_reply(uint32_t seq, int error, struct ofpbuf *fdb_query_data) { struct ofpbuf msg; int retval; @@ -390,6 +440,10 @@ send_reply(uint32_t seq, int error) BRC_GENL_C_DP_RESULT, 1); ((struct nlmsghdr *) msg.data)->nlmsg_seq = seq; nl_msg_put_u32(&msg, BRC_GENL_A_ERR_CODE, error); + if (fdb_query_data) { + nl_msg_put_unspec(&msg, BRC_GENL_A_FDB_DATA, + fdb_query_data->data, fdb_query_data->size); + } /* Send reply. */ retval = nl_sock_send(brc_sock, &msg, false); @@ -407,13 +461,13 @@ handle_bridge_cmd(struct ofpbuf *buffer, bool add) uint32_t seq; int error; - error = parse_command(buffer, &seq, &br_name, NULL); + error = parse_command(buffer, &seq, &br_name, NULL, NULL, NULL); if (!error) { error = add ? add_bridge(br_name) : del_bridge(br_name); if (!error) { error = rewrite_and_reload_config(); } - send_reply(seq, error); + send_reply(seq, error, NULL); } return error; } @@ -439,7 +493,7 @@ handle_port_cmd(struct ofpbuf *buffer, bool add) uint32_t seq; int error; - error = parse_command(buffer, &seq, &br_name, &port_name); + error = parse_command(buffer, &seq, &br_name, &port_name, NULL, NULL); if (!error) { if (!bridge_exists(br_name)) { VLOG_WARN("%s %s %s: no bridge named %s", @@ -458,12 +512,133 @@ handle_port_cmd(struct ofpbuf *buffer, bool add) VLOG_INFO("%s %s %s: success", cmd_name, br_name, port_name); error = rewrite_and_reload_config(); } - send_reply(seq, error); + send_reply(seq, error, NULL); } return error; } +static int +handle_fdb_query_cmd(struct ofpbuf *buffer) +{ + /* This structure is copied directly from the Linux 2.6.30 header files. + * It would be more straightforward to #include , but + * the 'port_hi' member was only introduced in Linux 2.6.26 and so systems + * with old header files won't have it. */ + struct __fdb_entry { + __u8 mac_addr[6]; + __u8 port_no; + __u8 is_local; + __u32 ageing_timer_value; + __u8 port_hi; + __u8 pad0; + __u16 unused; + }; + + struct mac { + uint8_t addr[6]; + }; + struct mac *local_macs; + int n_local_macs; + int i; + + struct ofpbuf query_data; + char *unixctl_command; + uint64_t count, skip; + const char *br_name; + struct svec ifaces; + char *output; + char *save_ptr; + uint32_t seq; + int error; + + /* Parse the command received from brcompat_mod. */ + error = parse_command(buffer, &seq, &br_name, NULL, &count, &skip); + if (error) { + return error; + } + + /* Fetch the forwarding database using ovs-appctl. */ + unixctl_command = xasprintf("fdb/show %s", br_name); + error = execute_appctl_command(unixctl_command, &output); + free(unixctl_command); + if (error) { + send_reply(seq, error, NULL); + return error; + } + + /* Fetch the MAC address for each interface on the bridge, so that we can + * fill in the is_local field in the response. */ + cfg_read(); + get_bridge_ifaces(br_name, &ifaces); + local_macs = xmalloc(ifaces.n * sizeof *local_macs); + n_local_macs = 0; + for (i = 0; i < ifaces.n; i++) { + const char *iface_name = ifaces.names[i]; + struct mac *mac = &local_macs[n_local_macs]; + if (!netdev_nodev_get_etheraddr(iface_name, mac->addr)) { + n_local_macs++; + } + } + svec_destroy(&ifaces); + + /* Parse the response from ovs-appctl and convert it to binary format to + * pass back to the kernel. */ + ofpbuf_init(&query_data, sizeof(struct __fdb_entry) * 8); + save_ptr = NULL; + strtok_r(output, "\n", &save_ptr); /* Skip header line. */ + while (count > 0) { + struct __fdb_entry *entry; + int port, vlan, age; + uint8_t mac[ETH_ADDR_LEN]; + char *line; + bool is_local; + + line = strtok_r(NULL, "\n", &save_ptr); + if (!line) { + break; + } + + if (sscanf(line, "%d %d "ETH_ADDR_SCAN_FMT" %d", + &port, &vlan, ETH_ADDR_SCAN_ARGS(mac), &age) + != 2 + ETH_ADDR_SCAN_COUNT + 1) { + struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + VLOG_INFO_RL(&rl, "fdb/show output has invalid format: %s", line); + continue; + } + + if (skip > 0) { + skip--; + continue; + } + + /* Is this the MAC address of an interface on the bridge? */ + is_local = false; + for (i = 0; i < n_local_macs; i++) { + if (eth_addr_equals(local_macs[i].addr, mac)) { + is_local = true; + break; + } + } + + entry = ofpbuf_put_uninit(&query_data, sizeof *entry); + memcpy(entry->mac_addr, mac, ETH_ADDR_LEN); + entry->port_no = port & 0xff; + entry->is_local = is_local; + entry->ageing_timer_value = age * HZ; + entry->port_hi = (port & 0xff00) >> 8; + entry->pad0 = 0; + entry->unused = 0; + count--; + } + free(output); + + send_reply(seq, 0, &query_data); + ofpbuf_uninit(&query_data); + + return 0; +} + static int brc_recv_update(void) { @@ -522,6 +697,10 @@ brc_recv_update(void) retval = handle_port_cmd(buffer, false); break; + case BRC_GENL_C_FDB_QUERY: + retval = handle_fdb_query_cmd(buffer); + break; + default: retval = EPROTO; } @@ -714,13 +893,34 @@ main(int argc, char *argv[]) return 0; } +static void +validate_appctl_command(void) +{ + const char *p; + int n; + + n = 0; + for (p = strchr(appctl_command, '%'); p; p = strchr(p + 2, '%')) { + if (p[1] == '%') { + /* Nothing to do. */ + } else if (p[1] == 's') { + n++; + } else { + ovs_fatal(0, "only '%%s' and '%%%%' allowed in --appctl-command"); + } + } + if (n != 1) { + ovs_fatal(0, "'%%s' must appear exactly once in --appctl-command"); + } +} + static void parse_options(int argc, char *argv[]) { enum { OPT_LOCK_TIMEOUT = UCHAR_MAX + 1, OPT_PRUNE_TIMEOUT, - OPT_RELOAD_COMMAND, + OPT_APPCTL_COMMAND, VLOG_OPTION_ENUMS, LEAK_CHECKER_OPTION_ENUMS }; @@ -729,7 +929,7 @@ parse_options(int argc, char *argv[]) {"version", no_argument, 0, 'V'}, {"lock-timeout", required_argument, 0, OPT_LOCK_TIMEOUT}, {"prune-timeout", required_argument, 0, OPT_PRUNE_TIMEOUT}, - {"reload-command", required_argument, 0, OPT_RELOAD_COMMAND}, + {"appctl-command", required_argument, 0, OPT_APPCTL_COMMAND}, DAEMON_LONG_OPTIONS, VLOG_LONG_OPTIONS, LEAK_CHECKER_LONG_OPTIONS, @@ -738,10 +938,9 @@ parse_options(int argc, char *argv[]) char *short_options = long_options_to_short_options(long_options); int error; - reload_command = xasprintf("%s/ovs-appctl -t " + appctl_command = xasprintf("%s/ovs-appctl -t " "%s/ovs-vswitchd.`cat %s/ovs-vswitchd.pid`.ctl " - "-e vswitchd/reload 2>&1 " - "| /usr/bin/logger -t brcompatd-reload", + "-e '%%s'", ovs_bindir, ovs_rundir, ovs_rundir); for (;;) { int c; @@ -768,8 +967,8 @@ parse_options(int argc, char *argv[]) prune_timeout = atoi(optarg) * 1000; break; - case OPT_RELOAD_COMMAND: - reload_command = optarg; + case OPT_APPCTL_COMMAND: + appctl_command = optarg; break; VLOG_OPTION_HANDLERS @@ -785,6 +984,8 @@ parse_options(int argc, char *argv[]) } free(short_options); + validate_appctl_command(); + argc -= optind; argv += optind; @@ -809,7 +1010,7 @@ usage(void) "CONFIG is the configuration file used by ovs-vswitchd.\n", program_name, program_name); printf("\nConfiguration options:\n" - " --reload-command=COMMAND shell command to reload ovs-vswitchd\n" + " --appctl-command=COMMAND shell command to run ovs-appctl\n" " --prune-timeout=SECS wait at most SECS before pruning ports\n" " --lock-timeout=MSECS wait at most MSECS for CONFIG to unlock\n" ); @@ -819,6 +1020,6 @@ usage(void) " -h, --help display this help message\n" " -V, --version display version information\n"); leak_checker_usage(); - printf("\nThe default reload command is:\n%s\n", reload_command); + printf("\nThe default appctl command is:\n%s\n", appctl_command); exit(EXIT_SUCCESS); } diff --git a/xenserver/etc_init.d_vswitch b/xenserver/etc_init.d_vswitch index 37269d6d..eba4baf0 100755 --- a/xenserver/etc_init.d_vswitch +++ b/xenserver/etc_init.d_vswitch @@ -187,13 +187,13 @@ function start_brcompatd { valgrind_opt="valgrind --log-file=$BRCOMPATD_VALGRIND_LOG $BRCOMPATD_VALGRIND_OPT" daemonize="n" fi - reload_cmd='/root/vswitch/bin/ovs-appctl -t /var/run/ovs-vswitchd.`cat /var/run/ovs-vswitchd.pid`.ctl -e vswitchd/reload 2>&1 | /usr/bin/logger -t brcompatd-reload' + appctl_cmd="$appctl -t /var/run/ovs-vswitchd.\`cat $VSWITCHD_PIDFILE\`.ctl -e '%s'" if [ "$daemonize" != "y" ]; then # Start in background and force a "success" message action "Starting ovs-brcompatd ($strace_opt$valgrind_opt)" true - (nice -n "$VSWITCHD_PRIORITY" $strace_opt $valgrind_opt "$brcompatd" --reload-command="$reload_cmd" -P$BRCOMPATD_PIDFILE -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF") & + (nice -n "$VSWITCHD_PRIORITY" $strace_opt $valgrind_opt "$brcompatd" --appctl-command="$appctl_cmd" -P$BRCOMPATD_PIDFILE -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF") & else - action "Starting ovs-brcompatd" nice -n "$BRCOMPATD_PRIORITY" $strace_opt $valgrind_opt "$brcompatd" --reload-command="$reload_cmd" -P$BRCOMPATD_PIDFILE -D -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF" + action "Starting ovs-brcompatd" nice -n "$BRCOMPATD_PRIORITY" $strace_opt $valgrind_opt "$brcompatd" --appctl-command="$appctl_cmd" -P$BRCOMPATD_PIDFILE -D -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF" fi } -- 2.30.2