1    	/*
2    	 * Copyright 2021-2023 the Pacemaker project contributors
3    	 *
4    	 * The version control history for this file may have further details.
5    	 *
6    	 * This source code is licensed under the GNU Lesser General Public License
7    	 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8    	 */
9    	
10   	#include <crm_internal.h>
11   	
12   	#include <stdlib.h>
13   	#include <time.h>
14   	
15   	#include <crm/crm.h>
16   	#include <crm/msg_xml.h>
17   	#include <crm/common/xml.h>
18   	#include <crm/common/ipc.h>
19   	#include <crm/common/ipc_internal.h>
20   	#include <crm/common/ipc_schedulerd.h>
21   	#include "crmcommon_private.h"
22   	
23   	typedef struct schedulerd_api_private_s {
24   	    char *client_uuid;
25   	} schedulerd_api_private_t;
26   	
27   	// \return Standard Pacemaker return code
28   	static int
29   	new_data(pcmk_ipc_api_t *api)
30   	{
31   	    struct schedulerd_api_private_s *private = NULL;
32   	
33   	    api->api_data = calloc(1, sizeof(struct schedulerd_api_private_s));
34   	
35   	    if (api->api_data == NULL) {
36   	        return errno;
37   	    }
38   	
39   	    private = api->api_data;
40   	    /* See comments in ipc_pacemakerd.c. */
41   	    private->client_uuid = pcmk__getpid_s();
42   	
43   	    return pcmk_rc_ok;
44   	}
45   	
46   	static void
47   	free_data(void *data)
48   	{
49   	    free(((struct schedulerd_api_private_s *) data)->client_uuid);
50   	    free(data);
51   	}
52   	
53   	// \return Standard Pacemaker return code
54   	static int
55   	post_connect(pcmk_ipc_api_t *api)
56   	{
57   	    if (api->api_data == NULL) {
58   	        return EINVAL;
59   	    }
60   	
61   	    return pcmk_rc_ok;
62   	}
63   	
64   	static bool
65   	reply_expected(pcmk_ipc_api_t *api, const xmlNode *request)
66   	{
67   	    const char *command = crm_element_value(request, F_CRM_TASK);
68   	
69   	    if (command == NULL) {
70   	        return false;
71   	    }
72   	
73   	    // We only need to handle commands that functions in this file can send
74   	    return pcmk__str_any_of(command, CRM_OP_PECALC, NULL);
75   	}
76   	
77   	static bool
78   	dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
79   	{
80   	    crm_exit_t status = CRM_EX_OK;
81   	    xmlNode *msg_data = NULL;
82   	    pcmk_schedulerd_api_reply_t reply_data = {
83   	        pcmk_schedulerd_reply_unknown
84   	    };
85   	    const char *value = NULL;
86   	
87   	    if (pcmk__str_eq((const char *) reply->name, "ack", pcmk__str_casei)) {
88   	        return false;
89   	    }
90   	
91   	    value = crm_element_value(reply, F_CRM_MSG_TYPE);
92   	    if (!pcmk__str_eq(value, XML_ATTR_RESPONSE, pcmk__str_none)) {
93   	        crm_info("Unrecognizable message from schedulerd: "
94   	                  "message type '%s' not '" XML_ATTR_RESPONSE "'",
95   	                  pcmk__s(value, ""));
96   	        status = CRM_EX_PROTOCOL;
97   	        goto done;
98   	    }
99   	
100  	    if (pcmk__str_empty(crm_element_value(reply, XML_ATTR_REFERENCE))) {
101  	        crm_info("Unrecognizable message from schedulerd: no reference");
102  	        status = CRM_EX_PROTOCOL;
103  	        goto done;
104  	    }
105  	
106  	    // Parse useful info from reply
107  	    msg_data = get_message_xml(reply, F_CRM_DATA);
108  	    value = crm_element_value(reply, F_CRM_TASK);
109  	
110  	    if (pcmk__str_eq(value, CRM_OP_PECALC, pcmk__str_none)) {
111  	        reply_data.reply_type = pcmk_schedulerd_reply_graph;
112  	        reply_data.data.graph.reference = crm_element_value(reply, XML_ATTR_REFERENCE);
113  	        reply_data.data.graph.input = crm_element_value(reply, F_CRM_TGRAPH_INPUT);
114  	        reply_data.data.graph.tgraph = msg_data;
115  	    } else {
116  	        crm_info("Unrecognizable message from schedulerd: "
117  	                  "unknown command '%s'", pcmk__s(value, ""));
118  	        status = CRM_EX_PROTOCOL;
119  	        goto done;
120  	    }
121  	
122  	done:
123  	    pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data);
124  	    return false;
125  	}
126  	
127  	pcmk__ipc_methods_t *
128  	pcmk__schedulerd_api_methods(void)
129  	{
130  	    pcmk__ipc_methods_t *cmds = calloc(1, sizeof(pcmk__ipc_methods_t));
131  	
132  	    if (cmds != NULL) {
133  	        cmds->new_data = new_data;
134  	        cmds->free_data = free_data;
135  	        cmds->post_connect = post_connect;
136  	        cmds->reply_expected = reply_expected;
137  	        cmds->dispatch = dispatch;
138  	    }
139  	    return cmds;
140  	}
141  	
142  	static int
143  	do_schedulerd_api_call(pcmk_ipc_api_t *api, const char *task, xmlNode *cib, char **ref)
144  	{
145  	    schedulerd_api_private_t *private;
146  	    xmlNode *cmd = NULL;
147  	    int rc;
148  	
(1) Event path: Condition "!pcmk_ipc_is_connected(api)", taking false branch.
149  	    if (!pcmk_ipc_is_connected(api)) {
150  	        return ENOTCONN;
151  	    }
152  	
153  	    private = api->api_data;
(2) Event path: Condition "!(private != NULL)", taking false branch.
154  	    CRM_ASSERT(private != NULL);
155  	
(3) Event path: Condition "crm_system_name", taking true branch.
156  	    cmd = create_request(task, cib, NULL, CRM_SYSTEM_PENGINE,
157  	                         crm_system_name? crm_system_name : "client",
158  	                         private->client_uuid);
159  	
(4) Event path: Condition "cmd", taking true branch.
160  	    if (cmd) {
161  	        rc = pcmk__send_ipc_request(api, cmd);
(5) Event path: Condition "rc != pcmk_rc_ok", taking true branch.
162  	        if (rc != pcmk_rc_ok) {
(6) Event path: Switch case default.
(7) Event path: Condition "trace_cs == NULL", taking true branch.
(8) Event path: Condition "crm_is_callsite_active(trace_cs, _level, 0)", taking false branch.
(9) Event path: Breaking from switch.
163  	            crm_debug("Couldn't send request to schedulerd: %s rc=%d",
164  	                      pcmk_rc_str(rc), rc);
165  	        }
166  	
(10) Event alloc_fn: Storage is returned from allocation function "strdup".
(11) Event assign: Assigning: "*ref" = "strdup(crm_element_value(cmd, "reference"))".
167  	        *ref = strdup(crm_element_value(cmd, F_CRM_REFERENCE));
168  	        free_xml(cmd);
(12) Event path: Falling through to end of if statement.
169  	    } else {
170  	        rc = ENOMSG;
171  	    }
172  	
173  	    return rc;
174  	}
175  	
176  	int
177  	pcmk_schedulerd_api_graph(pcmk_ipc_api_t *api, xmlNode *cib, char **ref)
178  	{
(1) Event alloc_arg: "do_schedulerd_api_call" allocates memory that is stored into "*ref". [details]
179  	    return do_schedulerd_api_call(api, CRM_OP_PECALC, cib, ref);
180  	}
181