--- /dev/null
+/* Copyright (c) 2015 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include <getopt.h>
+
+#include "command-line.h"
+#include "daemon.h"
+#include "fatal-signal.h"
+#include "openvswitch/vlog.h"
+#include "ovstest.h"
+#include "poll-loop.h"
+#include "unixctl.h"
+#include "util.h"
+
+VLOG_DEFINE_THIS_MODULE(test_unixctl);
+
+static void parse_options(int *argc, char **argvp[], char **unixctl_pathp);
+OVS_NO_RETURN static void usage(void);
+
+static void
+test_unixctl_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
+ const char *argv[] OVS_UNUSED, void *exiting_)
+{
+ bool *exiting = exiting_;
+ *exiting = true;
+ unixctl_command_reply(conn, NULL);
+}
+
+static void
+test_unixctl_echo(struct unixctl_conn *conn, int argc OVS_UNUSED,
+ const char *argv[], void *aux OVS_UNUSED)
+{
+ unixctl_command_reply(conn, argv[1]);
+}
+
+static void
+test_unixctl_echo_error(struct unixctl_conn *conn, int argc OVS_UNUSED,
+ const char *argv[], void *aux OVS_UNUSED)
+{
+ unixctl_command_reply_error(conn, argv[1]);
+}
+
+static void
+test_unixctl_log(struct unixctl_conn *conn, int argc OVS_UNUSED,
+ const char *argv[], void *aux OVS_UNUSED)
+{
+ VLOG_INFO("%s", argv[1]);
+ unixctl_command_reply(conn, NULL);
+}
+
+static void
+test_unixctl_block(struct unixctl_conn *conn OVS_UNUSED, int argc OVS_UNUSED,
+ const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+{
+ VLOG_INFO("%s", argv[1]);
+ unixctl_command_reply(conn, NULL);
+}
+
+static int
+test_unixctl_main(int argc, char *argv[])
+{
+ char *unixctl_path = NULL;
+ struct unixctl_server *unixctl;
+ bool exiting = false;
+
+ ovs_cmdl_proctitle_init(argc, argv);
+ set_program_name(argv[0]);
+ service_start(&argc, &argv);
+ fatal_ignore_sigpipe();
+ parse_options(&argc, &argv, &unixctl_path);
+
+ daemonize_start(false);
+ int retval = unixctl_server_create(unixctl_path, &unixctl);
+ if (retval) {
+ exit(EXIT_FAILURE);
+ }
+ unixctl_command_register("exit", "", 0, 0, test_unixctl_exit, &exiting);
+ unixctl_command_register("echo", "ARG", 1, 1, test_unixctl_echo, NULL);
+ unixctl_command_register("echo_error", "ARG", 1, 1,
+ test_unixctl_echo_error, NULL);
+ unixctl_command_register("log", "ARG", 1, 1, test_unixctl_log, NULL);
+ unixctl_command_register("block", "", 0, 0, test_unixctl_block, NULL);
+ daemonize_complete();
+
+ VLOG_INFO("Entering run loop.");
+ while (!exiting) {
+ unixctl_server_run(unixctl);
+ unixctl_server_wait(unixctl);
+ if (exiting) {
+ poll_immediate_wake();
+ }
+ poll_block();
+ }
+ unixctl_server_destroy(unixctl);
+
+ service_stop();
+ return 0;
+}
+
+static void
+parse_options(int *argcp, char **argvp[], char **unixctl_pathp)
+{
+ enum {
+ OPT_REMOTE = UCHAR_MAX + 1,
+ OPT_UNIXCTL,
+ VLOG_OPTION_ENUMS,
+ DAEMON_OPTION_ENUMS
+ };
+ static const struct option long_options[] = {
+ {"unixctl", required_argument, NULL, OPT_UNIXCTL},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ DAEMON_LONG_OPTIONS,
+ VLOG_LONG_OPTIONS,
+ {NULL, 0, NULL, 0},
+ };
+ char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
+ int argc = *argcp;
+ char **argv = *argvp;
+
+ for (;;) {
+ int c;
+
+ c = getopt_long(argc, argv, short_options, long_options, NULL);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case OPT_UNIXCTL:
+ *unixctl_pathp = optarg;
+ break;
+
+ case 'h':
+ usage();
+
+ case 'V':
+ ovs_print_version(0, 0);
+ exit(EXIT_SUCCESS);
+
+ VLOG_OPTION_HANDLERS
+ DAEMON_OPTION_HANDLERS
+
+ case '?':
+ exit(EXIT_FAILURE);
+
+ default:
+ abort();
+ }
+ }
+ free(short_options);
+
+ *argcp -= optind;
+ *argvp += optind;
+}
+
+static void
+usage(void)
+{
+ printf("%s: Open vSwitch unixctl test program\n"
+ "usage: %s [OPTIONS]\n",
+ program_name, program_name);
+ daemon_usage();
+ vlog_usage();
+ printf("\nOther options:\n"
+ " --unixctl=SOCKET override default control socket name\n"
+ " -h, --help display this help message\n"
+ " -V, --version display version information\n");
+ exit(EXIT_SUCCESS);
+}
+
+OVSTEST_REGISTER("test-unixctl", test_unixctl_main);
AT_CLEANUP
+m4_divert_push([PREPARE_TESTS])
+vlog_filt () {
+ sed 's/.*\(opened log file\).*/\1/
+s/.*|//' "$@"
+}
+m4_divert_pop([PREPARE_TESTS])
+
+AT_SETUP([vlog - vlog/reopen - C])
+# This test won't work as-is on Windows because Windows doesn't allow
+# files that are open to be renamed.
+AT_SKIP_IF([test "$IS_WIN32" = "yes"])
+on_exit 'kill `cat test-unixctl.pid`'
+
+AT_CAPTURE_FILE([log])
+AT_CAPTURE_FILE([log.old])
+AT_CHECK([ovstest test-unixctl --log-file=`pwd`/log --pidfile --detach],
+ [0], [], [stderr])
+AT_CHECK([vlog_filt stderr], [0],
+ [opened log file
+])
+
+AT_CHECK([APPCTL -t test-unixctl log message])
+mv log log.old
+AT_CHECK([APPCTL -t test-unixctl log message2])
+AT_CHECK([APPCTL -t test-unixctl vlog/reopen])
+AT_CHECK([APPCTL -t test-unixctl log message3])
+AT_CHECK([APPCTL -t test-unixctl exit])
+
+AT_CHECK([vlog_filt log.old], [0], [dnl
+opened log file
+Entering run loop.
+message
+message2
+closing log file
+])
+AT_CHECK([vlog_filt log], [0], [dnl
+opened log file
+message3
+])
+AT_CLEANUP
+
AT_SETUP([vlog - vlog/reopen - Python])
AT_SKIP_IF([test $HAVE_PYTHON = no])
on_exit 'kill `cat test-unixctl.py.pid`'
])
AT_CLEANUP
+AT_SETUP([vlog - vlog/reopen without log file - C])
+on_exit 'kill `cat test-unixctl.pid`'
+
+AT_CHECK([ovstest test-unixctl --pidfile --detach])
+
+AT_CHECK([APPCTL -t test-unixctl vlog/reopen], [2], [],
+ [Logging to file not configured
+ovs-appctl: test-unixctl: server returned an error
+])
+AT_CLEANUP
+
AT_SETUP([vlog - vlog/reopen without log file - Python])
AT_SKIP_IF([test $HAVE_PYTHON = no])
on_exit 'kill `cat test-unixctl.py.pid`'
])
AT_CLEANUP
+dnl This checks that if vlog/reopen can't reopen the log file,
+dnl nothing particularly bad (e.g. a crash) happens.
+AT_SETUP([vlog - vlog/reopen can't reopen log file - C])
+# Verify that /dev/full is a character device that fails writes.
+AT_SKIP_IF([test ! -c /dev/full])
+AT_SKIP_IF([echo > /dev/full])
+
+on_exit 'kill `cat test-unixctl.pid`'
+
+AT_CHECK([ovstest test-unixctl --log-file=`pwd`/log --pidfile --detach],
+ [0], [], [stderr])
+AT_CHECK([vlog_filt stderr], [0], [opened log file
+])
+
+AT_CHECK([APPCTL -t test-unixctl log message])
+mv log log.old
+ln -s /dev/full log
+AT_CHECK([APPCTL -t test-unixctl vlog/reopen])
+AT_CHECK([APPCTL -t test-unixctl log message2])
+rm log
+AT_CHECK([APPCTL -t test-unixctl vlog/reopen])
+AT_CHECK([APPCTL -t test-unixctl log message3])
+AT_CHECK([APPCTL -t test-unixctl exit])
+AT_CHECK([vlog_filt log.old], [0], [dnl
+opened log file
+Entering run loop.
+message
+closing log file
+])
+AT_CHECK([vlog_filt log], [0], [dnl
+opened log file
+message3
+])
+AT_CLEANUP
+
dnl This checks that if vlog/reopen can't reopen the log file,
dnl nothing particularly bad (e.g. Python throws an exception and
dnl aborts the program) happens.
])
AT_CLEANUP
+AT_SETUP([vlog - vlog/set and vlog/list - C])
+on_exit 'kill `cat test-unixctl.pid`'
+
+AT_CAPTURE_FILE([log])
+AT_CHECK([ovstest test-unixctl --log-file=`pwd`/log --pidfile --detach],
+ [0], [], [stderr])
+AT_CHECK([vlog_filt stderr], [0], [opened log file
+])
+
+AT_CHECK([APPCTL -t test-unixctl vlog/list | sed -n '1,2p
+/\(test_unixctl\|daemon\) /p'], [0], [dnl
+ console syslog file
+ ------- ------ ------
+daemon OFF INFO INFO
+test_unixctl OFF INFO INFO
+])
+
+AT_CHECK([APPCTL -t test-unixctl vlog/set daemon:syslog:err])
+AT_CHECK([APPCTL -t test-unixctl vlog/set file:dbg])
+AT_CHECK([APPCTL -t test-unixctl vlog/set nonexistent], [2], [],
+ [no destination, level, or module "nonexistent"
+ovs-appctl: test-unixctl: server returned an error
+])
+AT_CHECK([APPCTL -t test-unixctl vlog/list | sed -n '1,2p
+/\(test_unixctl\|daemon\) /p'], [0], [dnl
+ console syslog file
+ ------- ------ ------
+daemon OFF ERR DBG
+test_unixctl OFF INFO DBG
+])
+
+AT_CHECK([APPCTL -t test-unixctl vlog/set pattern], [2], [],
+ [missing destination
+ovs-appctl: test-unixctl: server returned an error
+])
+AT_CHECK([APPCTL -t test-unixctl vlog/set pattern:nonexistent], [2], [],
+ [unknown destination "nonexistent"
+ovs-appctl: test-unixctl: server returned an error
+])
+AT_CHECK([APPCTL -t test-unixctl vlog/set pattern:file:'I<3OVS|%m'])
+AT_CHECK([APPCTL -t test-unixctl log patterntest])
+AT_CHECK([grep -q 'I<3OVS' log])
+AT_CLEANUP
+
AT_SETUP([vlog - vlog/set and vlog/list - Python])
AT_SKIP_IF([test $HAVE_PYTHON = no])
on_exit 'kill `cat test-unixctl.py.pid`'