diff --git a/CHANGELOG.md b/CHANGELOG.md
index 850eb04d..ae81a95b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -39,7 +39,7 @@ Expected: April
* The netconf client will terminate (close the socket) if the client does not comply
* You can set `CLICON_NETCONF_HELLO_OPTIONAL` to true to use the old behavior of essentially ignoring hellos.
* New clixon-lib@2020-03-08.yang revision
- * Changed: RPC process-control output to choice dependent on operation
+ * Changed: RPC process-control output to choice with status fields
* New clixon-config@2020-03-08.yang revision
* Added: `CLICON_NETCONF_HELLO_OPTIONAL`
@@ -55,6 +55,7 @@ Developers may need to change their code
### Minor features
+* Added several fields to process-control status operation: active, description, command, status, starttime, pid
* Changed signal handling
* Moved clixon-proc sigchild handling from handler to clixon_events
* The base capability has been changed to "urn:ietf:params:netconf:base:1.1" following RFC6241.
diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c
index 9a7d4fa3..0dc24a30 100644
--- a/apps/backend/backend_client.c
+++ b/apps/backend/backend_client.c
@@ -1584,7 +1584,6 @@ from_client_process_control(clicon_handle h,
cxobj *x;
char *name = NULL;
char *opstr = NULL;
- uint32_t pid = 0;
proc_operation op = PROC_OP_NONE;
if ((x = xml_find_type(xe, NULL, "name", CX_ELMNT)) != NULL)
@@ -1594,16 +1593,16 @@ from_client_process_control(clicon_handle h,
op = clixon_process_op_str2int(opstr);
}
/* Make the actual process operation (with wrap function enabled) */
- if (clixon_process_operation(h, name, op, 1, &pid) < 0)
- goto done;
- if (op == PROC_OP_STATUS)
- cprintf(cbret, "%s%u",
- NETCONF_BASE_NAMESPACE,
- CLIXON_LIB_NS, pid?"true":"false",
- CLIXON_LIB_NS, pid);
- else
+ if (op == PROC_OP_STATUS){
+ if (clixon_process_status(h, name, cbret) < 0)
+ goto done;
+ }
+ else{
+ if (clixon_process_operation(h, name, op, 1) < 0)
+ goto done;
cprintf(cbret, "",
NETCONF_BASE_NAMESPACE, CLIXON_LIB_NS);
+ }
retval = 0;
done:
return retval;
diff --git a/apps/backend/backend_plugin_restconf.c b/apps/backend/backend_plugin_restconf.c
index 660fdfa6..8dd67a26 100644
--- a/apps/backend/backend_plugin_restconf.c
+++ b/apps/backend/backend_plugin_restconf.c
@@ -137,6 +137,7 @@ restconf_pseudo_process_control(clicon_handle h)
argv[i++] = NULL;
assert(i==nr);
if (clixon_process_register(h, RESTCONF_PROCESS,
+ "Clixon RESTCONF process",
NULL /* XXX network namespace */,
restconf_rpc_wrapper,
argv, nr) < 0)
@@ -196,7 +197,7 @@ restconf_pseudo_process_commit(clicon_handle h,
if ((cx = xpath_first(xtarget, NULL, "/restconf/enable")) != NULL &&
xml_flag(cx, XML_FLAG_CHANGE|XML_FLAG_ADD)){
if (clixon_process_operation(h, RESTCONF_PROCESS,
- enabled?PROC_OP_START:PROC_OP_STOP, 0, NULL) < 0)
+ enabled?PROC_OP_START:PROC_OP_STOP, 0) < 0)
goto done;
}
else if (enabled){ /* If something changed and running, restart process */
@@ -209,7 +210,7 @@ restconf_pseudo_process_commit(clicon_handle h,
* Specifically, the socket is terminated where the reply is sent, which will
* cause the curl to fail.
*/
- if (clixon_process_operation(h, RESTCONF_PROCESS, PROC_OP_RESTART, 0, NULL) < 0)
+ if (clixon_process_operation(h, RESTCONF_PROCESS, PROC_OP_RESTART, 0) < 0)
goto done;
}
}
diff --git a/lib/clixon/clixon_proc.h b/lib/clixon/clixon_proc.h
index b2690442..6c8b1a05 100644
--- a/lib/clixon/clixon_proc.h
+++ b/lib/clixon/clixon_proc.h
@@ -62,9 +62,10 @@ int clixon_proc_socket(char **argv, pid_t *pid, int *sock);
int clixon_proc_socket_close(pid_t pid, int sock);
int clixon_proc_background(char **argv, const char *netns, pid_t *pid);
proc_operation clixon_process_op_str2int(char *opstr);
-int clixon_process_register(clicon_handle h, const char *name, const char *netns, proc_cb_t *callback, char **argv, int argc);
+int clixon_process_register(clicon_handle h, const char *name, const char *descr, const char *netns, proc_cb_t *callback, char **argv, int argc);
int clixon_process_delete_all(clicon_handle h);
-int clixon_process_operation(clicon_handle h, const char *name, proc_operation op, const int wrapit, uint32_t *pid);
+int clixon_process_operation(clicon_handle h, const char *name, proc_operation op, const int wrapit);
+int clixon_process_status(clicon_handle h, const char *name, cbuf *cbret);
int clixon_process_start_all(clicon_handle h);
int clixon_process_sched_register(clicon_handle h);
int clixon_process_waitpid(clicon_handle h);
diff --git a/lib/src/clixon_event.c b/lib/src/clixon_event.c
index 1a0a2622..2ab4ed79 100644
--- a/lib/src/clixon_event.c
+++ b/lib/src/clixon_event.c
@@ -52,6 +52,8 @@
#include
#include
+#include
+
#include "clixon_queue.h"
#include "clixon_log.h"
#include "clixon_hash.h"
diff --git a/lib/src/clixon_proc.c b/lib/src/clixon_proc.c
index 455ddd11..93e05f4e 100644
--- a/lib/src/clixon_proc.c
+++ b/lib/src/clixon_proc.c
@@ -71,10 +71,14 @@
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
+#include "clixon_options.h"
#include "clixon_event.h"
#include "clixon_sig.h"
#include "clixon_string.h"
#include "clixon_queue.h"
+#include "clixon_yang.h"
+#include "clixon_xml.h"
+#include "clixon_netconf_lib.h"
#include "clixon_proc.h"
/*
@@ -85,6 +89,7 @@
struct process_entry_t {
qelem_t pe_qelem; /* List header */
char *pe_name; /* Name of process used for internal use. Unique with exiting=0 */
+ char *pe_description; /* Description of service */
char *pe_netns; /* Network namespace */
char **pe_argv; /* argv with command as element 0 and NULL-terminated */
int pe_argc; /* Length of argc */
@@ -93,6 +98,7 @@ struct process_entry_t {
int pe_clone; /* Duplicate when restarting, delete when reaped */
pid_t pe_status; /* Status on exit as defined in waitpid */
proc_operation pe_op; /* Operation pending? */
+ struct timeval pe_starttime; /* Start time */
proc_cb_t *pe_callback; /* Wrapper function, may be called from process_operation */
};
@@ -336,6 +342,10 @@ clixon_process_register_dup(process_entry_t *pe0,
clicon_err(OE_DB, errno, "strdup name");
goto done;
}
+ if (pe0->pe_description && (pe1->pe_description = strdup(pe0->pe_description)) == NULL){
+ clicon_err(OE_DB, errno, "strdup name");
+ goto done;
+ }
if (pe0->pe_netns && (pe1->pe_netns = strdup(pe0->pe_netns)) == NULL){
clicon_err(OE_DB, errno, "strdup netns");
goto done;
@@ -363,6 +373,7 @@ clixon_process_register_dup(process_entry_t *pe0,
*
* @param[in] h Clixon handle
* @param[in] name Process name
+ * @param[in] description Description of process
* @param[in] netns Namespace netspace (or NULL)
* @param[in] callback
* @param[in] argv NULL-terminated vector of vectors
@@ -374,6 +385,7 @@ clixon_process_register_dup(process_entry_t *pe0,
int
clixon_process_register(clicon_handle h,
const char *name,
+ const char *description,
const char *netns,
proc_cb_t *callback,
char **argv,
@@ -400,6 +412,10 @@ clixon_process_register(clicon_handle h,
clicon_err(OE_DB, errno, "strdup name");
goto done;
}
+ if (description && (pe->pe_description = strdup(description)) == NULL){
+ clicon_err(OE_DB, errno, "strdup description");
+ goto done;
+ }
if (netns && (pe->pe_netns = strdup(netns)) == NULL){
clicon_err(OE_DB, errno, "strdup netns");
goto done;
@@ -430,6 +446,8 @@ clixon_process_delete_only(process_entry_t *pe)
if (pe->pe_name)
free(pe->pe_name);
+ if (pe->pe_description)
+ free(pe->pe_description);
if (pe->pe_netns)
free(pe->pe_netns);
if (pe->pe_argv){
@@ -488,13 +506,12 @@ proc_op_run(pid_t pid0,
return retval;
}
-/*! Find process operation entry given name and op and perform operation if found
+/*! Find process entry given name and schedule operation
*
* @param[in] h clicon handle
* @param[in] name Name of process
* @param[in] op start, stop, restart, status
* @param[in] wrapit If set, call potential callback, if false, dont call it
- * @param[out] pid >0 process# and is running / 0: not running
* @retval -1 Error
* @retval 0 OK
* @see upgrade_callback_reg_fn which registers the callbacks
@@ -507,8 +524,7 @@ int
clixon_process_operation(clicon_handle h,
const char *name,
proc_operation op,
- int wrapit,
- uint32_t *pid)
+ int wrapit)
{
int retval = -1;
process_entry_t *pe;
@@ -520,25 +536,16 @@ clixon_process_operation(clicon_handle h,
pe = _proc_entry_list;
do {
if (strcmp(pe->pe_name, name) == 0){
- if (op == PROC_OP_STATUS){
- if (pe->pe_clone)
- continue; /* this may be a dying duplicate */
- if (pid)
- *pid = pe->pe_pid;
- }
- else {
- /* Call wrapper function that eg changes op based on config */
- if (wrapit && pe->pe_callback != NULL)
- if (pe->pe_callback(h, pe, &op) < 0)
- goto done;
- clicon_debug(1, "%s name: %s pid:%d op: %s", __FUNCTION__,
- name, pe->pe_pid, clicon_int2str(proc_operation_map, op));
- if (op == PROC_OP_START || op == PROC_OP_STOP || op == PROC_OP_RESTART){
- pe->pe_op = op;
- clicon_debug(1, "%s scheduling %s pid:%d", __FUNCTION__, name, pe->pe_pid);
- sched++;
- }
-
+ /* Call wrapper function that eg changes op based on config */
+ if (wrapit && pe->pe_callback != NULL)
+ if (pe->pe_callback(h, pe, &op) < 0)
+ goto done;
+ clicon_debug(1, "%s name: %s pid:%d op: %s", __FUNCTION__,
+ name, pe->pe_pid, clicon_int2str(proc_operation_map, op));
+ if (op == PROC_OP_START || op == PROC_OP_STOP || op == PROC_OP_RESTART){
+ pe->pe_op = op;
+ clicon_debug(1, "%s scheduling %s pid:%d", __FUNCTION__, name, pe->pe_pid);
+ sched++;
}
break; /* hit break here */
}
@@ -553,6 +560,66 @@ clixon_process_operation(clicon_handle h,
return retval;
}
+/*! Get process status according to clixon-lib.yang
+ *
+ * @param[in] h clicon handle
+ * @param[in] name Name of process
+ * @param[out] cbret XML status string
+ * @retval -1 Error
+ * @retval 0 OK
+ */
+int
+clixon_process_status(clicon_handle h,
+ const char *name,
+ cbuf *cbret)
+
+{
+ int retval = -1;
+ process_entry_t *pe;
+ int run;
+ int i;
+ char timestr[28];
+
+ if (_proc_entry_list == NULL)
+ goto ok;
+ pe = _proc_entry_list;
+ do {
+ if (strcmp(pe->pe_name, name) == 0){
+ if (pe->pe_clone)
+ continue; /* this may be a dying duplicate */
+ /* Check if running */
+ run = 0;
+ if (pe->pe_pid && proc_op_run(pe->pe_pid, &run) < 0)
+ goto done;
+ cprintf(cbret, "%s",
+ NETCONF_BASE_NAMESPACE, CLIXON_LIB_NS, run?"true":"false");
+ if (pe->pe_description)
+ cprintf(cbret, "%s", CLIXON_LIB_NS, pe->pe_description);
+ if (pe->pe_pid)
+ cprintf(cbret, "%u", CLIXON_LIB_NS, pe->pe_pid);
+ cprintf(cbret, "", CLIXON_LIB_NS);
+ for (i=0; ipe_argc-1; i++){
+ if (i)
+ cprintf(cbret, " ");
+ cprintf(cbret, "%s", pe->pe_argv[i]);
+ }
+ cprintf(cbret, "");
+ cprintf(cbret, "%u", CLIXON_LIB_NS, pe->pe_status);
+ if (run && time2str(pe->pe_starttime, timestr, sizeof(timestr)) < 0)
+ goto done;
+ cprintf(cbret, "%s", CLIXON_LIB_NS, timestr);
+ cprintf(cbret, "");
+ break; /* hit break here */
+ }
+ pe = NEXTQ(process_entry_t *, pe);
+ } while (pe != _proc_entry_list);
+ ok:
+ retval = 0;
+ done:
+ clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
+ return retval;
+}
+
/*! Go through process list and start all processes that are enabled via config wrap function
* @param[in] h Clixon handle
* Commit rules should have done this, but there are some cases such as backend -s none mode
@@ -623,7 +690,6 @@ clixon_process_sched(int fd,
pe->pe_exiting == 0){
/* Check if running */
run = 0;
- clicon_debug(1, "%s run: %d", __FUNCTION__, run);
if (proc_op_run(pe->pe_pid, &run) < 0)
goto done;
switch (op){
@@ -642,6 +708,7 @@ clixon_process_sched(int fd,
break;
if (clixon_proc_background(pe->pe_argv, pe->pe_netns, &newpid) < 0)
goto done;
+ gettimeofday(&pe->pe_starttime, NULL);
clicon_debug(1, "%s restart pid:%d -> %d", __FUNCTION__, pe->pe_pid, newpid);
/* Create a new pe */
if (clixon_process_register_dup(pe, &pe1) < 0)
@@ -655,6 +722,7 @@ clixon_process_sched(int fd,
break;
if (clixon_proc_background(pe->pe_argv, pe->pe_netns, &pe->pe_pid) < 0)
goto done;
+ gettimeofday(&pe->pe_starttime, NULL);
clicon_debug(1, "%s started pid:%d", __FUNCTION__, pe->pe_pid);
break;
default:
diff --git a/test/test_restconf_rpc.sh b/test/test_restconf_rpc.sh
index 5a7718ba..4de3cac9 100755
--- a/test/test_restconf_rpc.sh
+++ b/test/test_restconf_rpc.sh
@@ -76,22 +76,18 @@ $DEFAULTHELLO
EOF
)
- expect1=""
- match=$(echo "$ret" | grep --null -Go "$expect1")
- if [ -z "$match" ]; then
- err "$expect1" "$ret"
- fi
-
# >&2 echo "ret:$ret" # debug
-
- expect2="]]>]]>"
- match=$(echo "$ret" | grep --null -Go "$expect2")
+
+ expect1="[0-9]*"
+ match=$(echo "$ret" | grep --null -Go "$expect1")
+# >&2 echo "match:$match" # debug
if [ -z "$match" ]; then
- err "$expect2" "$ret"
+ pid=0
+ else
+ pid=$(echo "$match" | awk -F'[<>]' '{print $3}')
fi
- new "check rpc $operation get pid"
- pid=$(echo "$ret" | awk -F'[<>]' '{print $5}')
>&2 echo "pid:$pid" # debug
+
if [ -z "$pid" ]; then
err "Running process" "$ret"
fi
@@ -107,9 +103,9 @@ EOF
err "Running process"
fi
fi
+ echo "$pid" # cant use return that only uses 0-255
fi
sleep $DEMSLEEP
- echo "$pid" # cant use return that only uses 0-255
}
new "ENABLE true"
@@ -141,8 +137,8 @@ if [ $BE -ne 0 ]; then
fi
# For debug
->&2 echo "curl $CURLOPTS -X POST -H \"Content-Type: application/yang-data+json\" $RCPROTO://localhost/restconf/operations/clixon-lib:process-control -d '{\"clixon-lib:input\":{\"name\":\"restconf\",\"operation\":\"status\"}}'"
-exit
+#>&2 echo "curl $CURLOPTS -X POST -H \"Content-Type: application/yang-data+json\" $RCPROTO://localhost/restconf/operations/clixon-lib:process-control -d '{\"clixon-lib:input\":{\"name\":\"restconf\",\"operation\":\"status\"}}'"
+
# Get pid of running process and check return xml
new "1. Get rpc status"
pid0=$(testrpc status 1) # Save pid0
@@ -165,7 +161,7 @@ new "wait restconf"
wait_restconf
new "try restconf rpc status"
-expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/operations/clixon-lib:process-control -d '{"clixon-lib:input":{"name":"restconf","operation":"status"}}')" 0 "HTTP/1.1 200 OK" '{"clixon-lib:output":{"pid":'
+expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/operations/clixon-lib:process-control -d '{"clixon-lib:input":{"name":"restconf","operation":"status"}}')" 0 "HTTP/1.1 200 OK" '{"clixon-lib:output":' '"active":' '"pid":'
new "2. Get status"
pid1=$(testrpc status 1)
@@ -177,7 +173,7 @@ if [ "$pid0" -ne "$pid1" ]; then
fi
new "try restconf rpc restart"
-expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/operations/clixon-lib:process-control -d '{"clixon-lib:input":{"name":"restconf","operation":"restart"}}')" 0 "HTTP/1.1 200 OK" '{"clixon-lib:output":{"pid":'
+expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/operations/clixon-lib:process-control -d '{"clixon-lib:input":{"name":"restconf","operation":"restart"}}')" 0 "HTTP/1.1 204 No Content"
new "3. Get status"
pid1=$(testrpc status 1)
diff --git a/yang/clixon/clixon-lib@2021-03-08.yang b/yang/clixon/clixon-lib@2021-03-08.yang
index 9bdd90b0..cff8808b 100644
--- a/yang/clixon/clixon-lib@2021-03-08.yang
+++ b/yang/clixon/clixon-lib@2021-03-08.yang
@@ -3,6 +3,9 @@ module clixon-lib {
namespace "http://clicon.org/lib";
prefix cl;
+ import ietf-yang-types {
+ prefix yang;
+ }
organization
"Clicon / Clixon";
@@ -178,13 +181,28 @@ module clixon-lib {
case status {
description
"Output from status rpc";
- leaf status {
+ leaf active {
description "True if process is running, false if not";
type boolean;
}
+ leaf description {
+ type string;
+ description "Description of process. This is a static string";
+ }
+ leaf command {
+ type string;
+ description "Start command with arguments";
+ }
+ leaf status {
+ description "Exit Status as defined by waitpid() - if exited";
+ type int32;
+ }
+ leaf starttime {
+ description "Time of starting process UTC";
+ type yang:date-and-time;
+ }
leaf pid {
- description "Process-id of running process or 0 if not running
- Value is only valid for operation status";
+ description "Process-id of main running process (if active)";
type uint32;
}
}