1 /*----------------------------------------------------------------------------*/
2 /* Xymon message daemon.                                                      */
3 /*                                                                            */
4 /* Client backend module for Linux                                            */
5 /*                                                                            */
6 /* Copyright (C) 2005-2011 Henrik Storner <henrik@hswn.dk>                    */
7 /*                                                                            */
8 /* This program is released under the GNU General Public License (GPL),       */
9 /* version 2. See the file "COPYING" for details.                             */
10 /*                                                                            */
11 /*----------------------------------------------------------------------------*/
12 
13 static char linux_rcsid[] = "$Id: linux.c 7886 2016-02-02 20:16:19Z jccleaver $";
14 
handle_linux_client(char * hostname,char * clienttype,enum ostype_t os,void * hinfo,char * sender,time_t timestamp,char * clientdata)15 void handle_linux_client(char *hostname, char *clienttype, enum ostype_t os,
16 			 void *hinfo, char *sender, time_t timestamp,
17 			 char *clientdata)
18 {
19 	char *timestr;
20 	char *uptimestr;
21 	char *clockstr;
22 	char *msgcachestr;
23 	char *whostr;
24 	char *psstr;
25 	char *topstr;
26 	char *dfstr;
27 	char *inodestr;
28 	char *freestr;
29 	char *msgsstr;
30 	char *netstatstr;
31 	char *vmstatstr;
32 	char *ifstatstr;
33 	char *portsstr;
34 	char *mdstatstr;
35 
36 	char fromline[1024];
37 
38 	sprintf(fromline, "\nStatus message received from %s\n", sender);
39 
40 	splitmsg(clientdata);
41 
42 	timestr = getdata("date");
43 	uptimestr = getdata("uptime");
44 	clockstr = getdata("clock");
45 	msgcachestr = getdata("msgcache");
46 	whostr = getdata("who");
47 	psstr = getdata("ps");
48 	topstr = getdata("top");
49 	dfstr = getdata("df");
50 	inodestr = getdata("inode");
51 	freestr = getdata("free");
52 	msgsstr = getdata("msgs");
53 	netstatstr = getdata("netstat");
54 	ifstatstr = getdata("ifstat");
55 	vmstatstr = getdata("vmstat");
56 	portsstr = getdata("ports");
57 	mdstatstr = getdata("mdstat");
58 
59 	unix_cpu_report(hostname, clienttype, os, hinfo, fromline, timestr, uptimestr, clockstr, msgcachestr,
60 			whostr, 0, psstr, 0, topstr);
61 	unix_disk_report(hostname, clienttype, os, hinfo, fromline, timestr, "Available", "Capacity", "Mounted", dfstr);
62 	unix_inode_report(hostname, clienttype, os, hinfo, fromline, timestr, "IFree", "IUse%", "Mounted", inodestr);
63 	unix_procs_report(hostname, clienttype, os, hinfo, fromline, timestr, "CMD", NULL, psstr);
64 	unix_ports_report(hostname, clienttype, os, hinfo, fromline, timestr, 3, 4, 5, portsstr);
65 
66 	msgs_report(hostname, clienttype, os, hinfo, fromline, timestr, msgsstr);
67 	file_report(hostname, clienttype, os, hinfo, fromline, timestr);
68 	linecount_report(hostname, clienttype, os, hinfo, fromline, timestr);
69 	deltacount_report(hostname, clienttype, os, hinfo, fromline, timestr);
70 
71 	unix_netstat_report(hostname, clienttype, os, hinfo, fromline, timestr, netstatstr);
72 	unix_ifstat_report(hostname, clienttype, os, hinfo, fromline, timestr, ifstatstr);
73 	unix_vmstat_report(hostname, clienttype, os, hinfo, fromline, timestr, vmstatstr);
74 
75 	/*
76 	 * Sigh. Recent kernels + procps-ng change things up a bit. If 'available' is present
77 	 * (roughly, 3.14+ and 2.6.27+, but depends on the vendor), then we'll use the inverse of that:
78 	 * 	(Physical - Available = ACTUALUSED)
79 	 * Otherwise, it's:
80 	 *	(Physical Used - (buffers + cached) = ACTUALUSED)
81 	 *
82 	 * See discussions at http://lists.xymon.com/pipermail/xymon/2015-April/041628.html
83 	 * If the legacy meminfo display is NOT used, we should get the old format still
84 	 *
85 	 */
86 
87 	if (freestr) {
88 		char *p;
89 		long memphystotal, memphysused, memphysfree,
90 		     memacttotal, memactused, memactfree,
91 		     memswaptotal, memswapused, memswapfree;
92 
93 		memphystotal = memswaptotal = memphysused = memswapused = memacttotal = memactused = memactfree = -1;
94 
95 		/* check for old style */
96 		p = strstr(freestr, "\n-/+ buffers/cache:");
97 		if (p) {
98 			p = strstr(freestr, "\nMem:");
99 			if (p && (sscanf(p, "\nMem: %ld %ld %ld", &memphystotal, &memphysused, &memphysfree) == 3)) {
100 				memphystotal /= 1024;
101 				memphysused /= 1024;
102 				memphysfree /= 1024;
103 			}
104 			p = strstr(freestr, "\n-/+ buffers/cache:");
105 			if (sscanf(p, "\n-/+ buffers/cache: %ld %ld", &memactused, &memactfree) == 2) {
106 				memacttotal = memphystotal;
107 				memactused /= 1024;
108 				memactfree /= 1024;
109 			}
110 
111 		}
112 		/* check for new style */
113 		else if (strstr(freestr, "available\n")) {
114 			long shared, buffcache;
115 			p = strstr(freestr, "\nMem:");
116 			if (p && (sscanf(p, "\nMem: %ld %ld %ld %ld %ld %ld", &memphystotal, &memphysused, &memphysfree,
117 										&shared, &buffcache, &memactfree) == 6)) {
118 				memphystotal /= 1024;
119 				memphysused /= 1024;
120 				memphysfree /= 1024;
121 				/* Provide a Physical Used value that's compatible with previous thresholds. However, use the */
122 				/* new 'Available' line as the basis for "Actual Used", since it'll be more accurate. */
123 				memacttotal = memphystotal;
124 				memactfree /= 1024;
125 				memactused = memacttotal - memactfree; if (memactused < 0) memactused = 0;
126 				memphysused += (buffcache / 1024);
127 
128 			}
129 		}
130 		else errprintf(" -> No readable memory data for %s in freestr\n", hostname);
131 
132 		/* There's always a swap line */
133 		p = strstr(freestr, "\nSwap:");
134 		if (p && (sscanf(p, "\nSwap: %ld %ld %ld", &memswaptotal, &memswapused, &memswapfree) == 3)) {
135 			memswaptotal /= 1024;
136 			memswapused /= 1024;
137 			memswapfree /= 1024;
138 		}
139 
140 		unix_memory_report(hostname, clienttype, os, hinfo, fromline, timestr,
141 				   memphystotal, memphysused,
142 				   memacttotal, memactused,
143 				   memswaptotal, memswapused);
144 	}
145 
146 	if (mdstatstr) {
147 		char *statcopy, *bol, *eol;
148 		int color = COL_GREEN;
149 		char *mdname = NULL, *mdstatus = NULL;
150 		int mddevices = 0, mdactive = 0, recovering = 0;
151 		strbuffer_t *alerttext = newstrbuffer(0);
152 		char msgline[1024];
153 		char *summary = NULL;
154 		int arraycount = 0;
155 
156 		statcopy = (char *)malloc(strlen(mdstatstr) + 10);
157 		sprintf(statcopy, "%s\nmd999\n", mdstatstr);
158 
159 		bol = statcopy;
160 		while (bol) {
161 			eol = strchr(bol, '\n'); if (eol) *eol = '\0';
162 
163 			if ((strncmp(bol, "md", 2) == 0) && (isdigit(*(bol+2)))) {
164 				char *tok;
165 
166 				if (mdname && (mddevices >= 0) && (mdactive >= 0)) {
167 					int onecolor = COL_GREEN;
168 
169 					/* Got a full md device status, flush it before we start on the next one */
170 					arraycount++;
171 					if (mddevices != mdactive) {
172 						if (!recovering) {
173 							onecolor = COL_RED;
174 							snprintf(msgline, sizeof(msgline), "&red %s : Disk failure in array : %d devices of %d active\n", mdname, mdactive, mddevices);
175 							addtobuffer(alerttext, msgline);
176 							summary = "failure";
177 						}
178 						else {
179 							onecolor = COL_YELLOW;
180 							snprintf(msgline, sizeof(msgline), "&yellow %s status %s : %d devices of %d active\n", mdname, mdstatus, mdactive, mddevices);
181 							addtobuffer(alerttext, msgline);
182 							if (!summary) summary = "recovering";
183 						}
184 					}
185 					else {
186 						snprintf(msgline, sizeof(msgline), "&green %s : %d devices of %d active\n", mdname, mdactive, mddevices);
187 						addtobuffer(alerttext, msgline);
188 					}
189 
190 					if (onecolor > color) {
191 						color = onecolor;
192 					}
193 				}
194 
195 				/* First line, holds the name of the array and the active/inactive status */
196 				mddevices = mdactive = -1; recovering = 0;
197 
198 				mdname = strtok(bol, " ");
199 				tok = strtok(NULL, " ");	// Skip the ':'
200 				mdstatus = strtok(NULL, " ");
201 			}
202 
203 
204 			if (mdname && ((mddevices == -1) && (mdactive == -1)) && (strchr(bol, '/'))) {
205 				char *p = strchr(bol, '/');
206 
207 				/* Second line: Holds the number of configured/active devices */
208 				mdactive = atoi(p+1);
209 				while ((p > bol) && (isdigit(*(p-1)))) p--;
210 				mddevices = atoi(p);
211 			}
212 
213 			if (mdname && (mddevices != mdactive) && strstr(bol, "recovery = ")) {
214 				/* Third line: Only present during recovery */
215 				mdstatus = "recovery in progress";
216 				recovering = 1;
217 			}
218 
219 			bol = (eol ? eol+1 : NULL);
220 		}
221 
222 
223 		if (arraycount > 0) {
224 			init_status(color);
225 			sprintf(msgline, "status %s.raid %s %s - RAID %s\n\n",
226 				commafy(hostname), colorname(color),
227 				(timestr ? timestr : "<No timestamp data>"),
228 				(summary ? summary : "OK"));
229 			addtostatus(msgline);
230 			if (STRBUFLEN(alerttext) > 0) {
231 				addtostrstatus(alerttext);
232 				addtostatus("\n\n");
233 			}
234 			addtostatus("============================ /proc/mdstat ===========================\n\n");
235 			addtostatus(mdstatstr);
236 			finish_status();
237 		}
238 
239 		xfree(statcopy);
240 		freestrbuffer(alerttext);
241 	}
242 
243 	splitmsg_done();
244 }
245 
246