1    	/*
2    	 * Copyright 2010-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   	#ifndef _GNU_SOURCE
13   	#  define _GNU_SOURCE
14   	#endif
15   	
16   	#include <sys/types.h>
17   	#include <sys/stat.h>
18   	#include <sys/wait.h>
19   	#include <errno.h>
20   	#include <unistd.h>
21   	#include <dirent.h>
22   	#include <grp.h>
23   	#include <string.h>
24   	#include <sys/time.h>
25   	#include <sys/resource.h>
26   	
27   	#include "crm/crm.h"
28   	#include <crm/msg_xml.h>
29   	#include "crm/common/mainloop.h"
30   	#include "crm/services.h"
31   	
32   	#include "services_private.h"
33   	#include "services_nagios.h"
34   	
35   	/*!
36   	 * \internal
37   	 * \brief Prepare a Nagios action
38   	 *
39   	 * \param[in,out] op  Action to prepare
40   	 *
41   	 * \return Standard Pacemaker return code
42   	 */
43   	int
44   	services__nagios_prepare(svc_action_t *op)
45   	{
46   	    op->opaque->exec = pcmk__full_path(op->agent, NAGIOS_PLUGIN_DIR);
47   	    op->opaque->args[0] = strdup(op->opaque->exec);
48   	    if (op->opaque->args[0] == NULL) {
49   	        return ENOMEM;
50   	    }
51   	
52   	    if (pcmk__str_eq(op->action, PCMK_ACTION_MONITOR, pcmk__str_casei)
53   	        && (op->interval_ms == 0)) {
54   	
55   	        // Invoke --version for a nagios probe
56   	        op->opaque->args[1] = strdup("--version");
57   	        if (op->opaque->args[1] == NULL) {
58   	            return ENOMEM;
59   	        }
60   	
61   	    } else if (op->params != NULL) {
62   	        GHashTableIter iter;
63   	        char *key = NULL;
64   	        char *value = NULL;
65   	        int index = 1; // 0 is already set to executable name
66   	
67   	        g_hash_table_iter_init(&iter, op->params);
68   	
69   	        while (g_hash_table_iter_next(&iter, (gpointer *) & key,
70   	                                      (gpointer *) & value)) {
71   	
72   	            if (index > (PCMK__NELEM(op->opaque->args) - 2)) {
73   	                return E2BIG;
74   	            }
75   	
76   	            if (pcmk__str_eq(key, XML_ATTR_CRM_VERSION, pcmk__str_casei)
77   	                || strstr(key, CRM_META "_")) {
78   	                continue;
79   	            }
80   	
81   	            op->opaque->args[index++] = crm_strdup_printf("--%s", key);
82   	            op->opaque->args[index++] = strdup(value);
83   	            if (op->opaque->args[index - 1] == NULL) {
84   	                return ENOMEM;
85   	            }
86   	        }
87   	    }
88   	
89   	    // Nagios actions don't need to keep the parameters
90   	    if (op->params != NULL) {
91   	        g_hash_table_destroy(op->params);
92   	        op->params = NULL;
93   	    }
94   	    return pcmk_rc_ok;
95   	}
96   	
97   	/*!
98   	 * \internal
99   	 * \brief Map a Nagios result to a standard OCF result
100  	 *
101  	 * \param[in] exit_status  Nagios exit status
102  	 *
103  	 * \return Standard OCF result
104  	 */
105  	enum ocf_exitcode
106  	services__nagios2ocf(int exit_status)
107  	{
108  	    switch (exit_status) {
109  	        case NAGIOS_STATE_OK:
110  	            return PCMK_OCF_OK;
111  	
112  	        case NAGIOS_INSUFFICIENT_PRIV:
113  	            return PCMK_OCF_INSUFFICIENT_PRIV;
114  	
115  	        case NAGIOS_STATE_WARNING:
116  	            return PCMK_OCF_DEGRADED;
117  	
118  	        case NAGIOS_STATE_CRITICAL:
119  	        case NAGIOS_STATE_UNKNOWN:
120  	        default:
121  	            return PCMK_OCF_UNKNOWN_ERROR;
122  	    }
123  	}
124  	
125  	static inline char *
126  	nagios_metadata_name(const char *plugin)
127  	{
128  	    return crm_strdup_printf(NAGIOS_METADATA_DIR "/%s.xml", plugin);
129  	}
130  	
131  	GList *
132  	services__list_nagios_agents(void)
133  	{
134  	    GList *plugin_list = NULL;
135  	    GList *result = NULL;
136  	
137  	    plugin_list = services_os_get_directory_list(NAGIOS_PLUGIN_DIR, TRUE, TRUE);
138  	
139  	    // Return only the plugins that have metadata
140  	    for (GList *gIter = plugin_list; gIter != NULL; gIter = gIter->next) {
141  	        struct stat st;
142  	        const char *plugin = gIter->data;
143  	        char *metadata = nagios_metadata_name(plugin);
144  	
145  	        if (stat(metadata, &st) == 0) {
146  	            result = g_list_append(result, strdup(plugin));
147  	        }
148  	        free(metadata);
149  	    }
150  	    g_list_free_full(plugin_list, free);
151  	    return result;
152  	}
153  	
154  	gboolean
155  	services__nagios_agent_exists(const char *name)
156  	{
157  	    char *buf = NULL;
158  	    gboolean rc = FALSE;
159  	    struct stat st;
160  	
161  	    if (name == NULL) {
162  	        return rc;
163  	    }
164  	
165  	    buf = crm_strdup_printf(NAGIOS_PLUGIN_DIR "/%s", name);
166  	    if (stat(buf, &st) == 0) {
167  	        rc = TRUE;
168  	    }
169  	
170  	    free(buf);
171  	    return rc;
172  	}
173  	
174  	int
175  	services__get_nagios_metadata(const char *type, char **output)
176  	{
177  	    int rc = pcmk_ok;
178  	    FILE *file_strm = NULL;
179  	    int start = 0, length = 0, read_len = 0;
180  	    char *metadata_file = nagios_metadata_name(type);
181  	
182  	    file_strm = fopen(metadata_file, "r");
(1) Event path: Condition "file_strm == NULL", taking false branch.
183  	    if (file_strm == NULL) {
184  	        crm_err("Metadata file %s does not exist", metadata_file);
185  	        free(metadata_file);
186  	        return -EIO;
187  	    }
188  	
189  	    /* see how big the file is */
190  	    start = ftell(file_strm);
191  	    fseek(file_strm, 0L, SEEK_END);
192  	    length = ftell(file_strm);
193  	    fseek(file_strm, 0L, start);
194  	
(2) Event path: Condition "!(length >= 0)", taking false branch.
195  	    CRM_ASSERT(length >= 0);
(3) Event path: Condition "!(start == ftell(file_strm))", taking false branch.
196  	    CRM_ASSERT(start == ftell(file_strm));
197  	
(4) Event path: Condition "length <= 0", taking false branch.
198  	    if (length <= 0) {
199  	        crm_info("%s was not valid", metadata_file);
200  	        free(*output);
201  	        *output = NULL;
202  	        rc = -EIO;
203  	
204  	    } else {
(5) Event path: Switch case default.
(6) Event path: Condition "trace_cs == NULL", taking true branch.
(7) Event path: Condition "crm_is_callsite_active(trace_cs, _level, 0)", taking false branch.
(8) Event path: Breaking from switch.
205  	        crm_trace("Reading %d bytes from file", length);
206  	        *output = calloc(1, (length + 1));
(9) Event string_null_source: Function "fread" does not terminate string "*output".
207  	        read_len = fread(*output, 1, length, file_strm);
(10) Event path: Condition "read_len != length", taking false branch.
208  	        if (read_len != length) {
209  	            crm_err("Calculated and read bytes differ: %d vs. %d",
210  	                    length, read_len);
211  	            free(*output);
212  	            *output = NULL;
213  	            rc = -EIO;
214  	        }
215  	    }
216  	
217  	    fclose(file_strm);
218  	    free(metadata_file);
219  	    return rc;
220  	}
221