1/************************************************************************
2Copyright (c) 2015, The Linux Foundation. All rights reserved.
3
4Redistribution and use in source and binary forms, with or without
5modification, are permitted provided that the following conditions are
6met:
7    * Redistributions of source code must retain the above copyright
8      notice, this list of conditions and the following disclaimer.
9    * Redistributions in binary form must reproduce the above
10      copyright notice, this list of conditions and the following
11      disclaimer in the documentation and/or other materials provided
12      with the distribution.
13    * Neither the name of The Linux Foundation nor the names of its
14      contributors may be used to endorse or promote products derived
15      from this software without specific prior written permission.
16
17THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28************************************************************************/
29
30/**
31 * @file datatop_meminfo_file_poll.c
32 * @brief Adds ability for data collection from /proc/meminfo
33 *
34 * File contains methods for searching and polling data from
35 * "/proc/meminfo"
36 */
37
38#include <stdio.h>
39#include <stdint.h>
40#include <string.h>
41#include <stdlib.h>
42#include <inttypes.h>
43#include "datatop_interface.h"
44#include "datatop_fileops.h"
45#include "datatop_str.h"
46
47#define DTOP_MEM_SIZE 8192
48#define DTOP_MEM_LINE (DTOP_MEM_SIZE>>2)
49
50/**
51* @struct dtop_meminfo_vars
52* @brief Struct used to hold necessary variables for /proc/meminfo dpg
53*
54* @var dtop_meminfo_vars::line
55* Array of strings where necessary dp names and values are held.
56* @var dtop_meminfo_vars::line_count
57* Number of lines the file is that the dpg represents.
58*/
59struct dtop_meminfo_vars {
60	char **line;
61	int line_count;
62};
63
64/**
65 * @brief Parses lines with data in "/proc/meminfo"
66 *
67 * @param line1 Line to parse to find datapoint names and values.
68 * @param len1 Length of line1.
69 * @param l Index in the dictionary the key/value pair is added to.
70 * @param dict Dictionary the keys and values are added to.
71 */
72int dt_meminfo_parse(char *line1, int len1,
73			int l, struct dt_procdict *dict)
74{
75	int i, k, n;
76	if (len1 < 1)
77		return 0;
78
79	if (line1 == 0 || dict == 0)
80		return 0;
81
82	k = l;
83	dict->key[k] = &line1[0];
84	for (i = 0; i < len1 && k < DTOP_DICT_SIZE; i++) {
85		if (line1[i] == ' ' || line1[i] == '	') {
86			line1[i] = 0;
87			n = i+1;
88			while (line1[n] == '	' || line1[n] == ' ')
89				n++;
90			dict->val[k] = &line1[n];
91			while (line1[n] != ' ')
92				n++;
93			line1[n] = 0;
94			break;
95		}
96	}
97	k++;
98	dict->max = k;
99	return k;
100}
101
102/**
103 * @brief Stores the data collected from a "/proc/meminfo"
104 *
105 * @param dpg Struct that polled data is added to.
106 * @return DTOP_POLL_IO_ERR - Poll of dpg unsuccessful.
107 * @return DTOP_POLL_OK - Poll of dpg successful.
108 */
109int dtop_meminfo_poll(struct dtop_data_point_gatherer *dpg)
110{
111	char *data;
112	int *line_len = malloc(sizeof(int) *
113			((struct dtop_meminfo_vars *)
114			(dpg->priv))->line_count);
115	int read;
116	struct dt_procdict dict;
117	int i, j, n, sum;
118
119	read = dt_read_file(dpg->file, &data, DTOP_MEM_SIZE);
120	if (read == 0 || data == 0)
121		return DTOP_POLL_IO_ERR;
122
123	sum = 0;
124	/* Assigns each line read from the file, a length */
125	for (n = 0; n < ((struct dtop_meminfo_vars *)
126				(dpg->priv))->line_count; n++) {
127		line_len[n] = dt_read_line(((struct dtop_meminfo_vars *)
128					(dpg->priv))->line[n],
129					   DTOP_MEM_LINE, data,
130					   DTOP_MEM_SIZE, sum);
131		if (n <= (((struct dtop_meminfo_vars *)
132			(dpg->priv))->line_count - 1)) {
133			sum += (line_len[n] + 1);
134		}
135
136	}
137
138	/* Stores dp names and values in dictionary */
139	for (i = 0; i < dpg->data_points_len; i++)
140		dt_meminfo_parse(((struct dtop_meminfo_vars *)
141			(dpg->priv))->line[i], line_len[i], i, &dict);
142
143	/* Assigns the dp value to the dp struct */
144	for (j = 0; j < dpg->data_points_len; j++) {
145		i = dt_find_dict_idx(dpg->data_points[j].name, &dict);
146		if (i >= 0 && i < dict.max) {
147			sscanf(dict.val[i], "%" PRIu64,
148			       &(dpg->data_points[i].data.d_ulong));
149			dpg->data_points[i].data.d_ulong *= 1024;
150			if (dpg->data_points[i].
151				initial_data_populated == NOT_POPULATED) {
152				dpg->data_points[i].initial_data.d_ulong
153					= dpg->data_points[i].data.d_ulong;
154				dpg->data_points[i].initial_data_populated
155					= POPULATED;
156			}
157		}
158	}
159
160	dt_free(&data);
161	free(line_len);
162	return DTOP_POLL_OK;
163}
164
165/**
166 * @brief Frees dynamically allocated "/proc/meminfo" dpg.
167 *
168 * Frees the memory of the dpg along with it's data_points
169 * and other malloc'd memory no longer needed.
170 *
171 * @param dpg Dpg to deconstruct and deallocate memory for.
172 */
173static void dtop_meminfo_dpg_deconstructor
174			(struct dtop_data_point_gatherer *dpset)
175{
176	int i;
177	free(dpset->data_points);
178	for (i = 0; i < ((struct dtop_meminfo_vars *)
179				(dpset->priv))->line_count; i++)
180		free(((struct dtop_meminfo_vars *)(dpset->priv))->line[i]);
181	free(((struct dtop_meminfo_vars *)(dpset->priv))->line);
182	free(((struct dtop_meminfo_vars *)(dpset->priv)));
183	free(dpset);
184}
185
186/**
187 * @brief Creates a dpg for "/proc/meminfo" file
188 *
189 * Dynamically allocates memory for dpg which is then added to a linked list
190 * via the dtop_register(dpg) function call.
191 *
192 * @param data_points dtop_data_point struct that dpg points to.
193 * @param storage dtop_meminfo_vars struct that holds relevant dpg variables.
194 */
195static void construct_meminfo_file_dpg(struct dtop_data_point
196		*data_points, struct dtop_meminfo_vars *storage)
197{
198	struct dtop_data_point_gatherer *dpg = malloc
199		(sizeof(struct dtop_data_point_gatherer));
200	dpg->prefix = "/proc/meminfo";
201	dpg->file = "/proc/meminfo";
202	dpg->poll = dtop_meminfo_poll;
203	dpg->data_points = data_points;
204	dpg->priv = (struct dtop_meminfo_vars *)storage;
205	dpg->data_points_len = storage->line_count;
206	dpg->deconstruct = dtop_meminfo_dpg_deconstructor;
207
208	dtop_register(dpg);
209}
210
211/**
212 * @brief Scans "/proc/meminfo in order to autodetect dps.
213 *
214 * Searches through "/proc/meminfo" file for all available data
215 * points to create as dp structs.
216 *
217 * @param storage dtop_meminfo_vars struct where relevant variables are stored.
218 */
219int dtop_meminfo_search(struct dtop_meminfo_vars *storage)
220{
221	int i, k, n, sum;
222	char *data;
223	int *line_len = malloc(sizeof(int) * storage->line_count);
224	int read;
225	struct dt_procdict dict;
226	struct dtop_data_point *data_points;
227
228	storage->line = malloc(storage->line_count * sizeof(*storage->line));
229
230	for (i = 0; i < storage->line_count; i++)
231		storage->line[i] = malloc(sizeof(char) * DTOP_MEM_LINE);
232
233	read = dt_read_file("/proc/meminfo", &data, DTOP_MEM_SIZE);
234	if (read == 0 || data == 0)
235		return DTOP_POLL_IO_ERR;
236
237	sum = 0;
238	/* Assigns each line read from the file, a length */
239	for (n = 0; n < storage->line_count; n++) {
240		line_len[n] = dt_read_line(storage->line[n],
241					   DTOP_MEM_LINE, data,
242					   DTOP_MEM_SIZE, sum);
243		if (n < (storage->line_count - 1))
244			sum += (line_len[n] + 1);
245	}
246
247	/* Stores dp names in dictionary */
248	for (i = 0; i < (storage->line_count); i++)
249		dt_parse_proc_same_line_key_and_val(storage->line[i],
250						line_len[i], i, &dict);
251
252	data_points = malloc
253		       (storage->line_count * sizeof(struct dtop_data_point));
254
255	k = 0;
256	/* Creates a dtop_data_point struct for each dp found in the file */
257	for (i = 0; i < dict.max; i++) {
258		data_points[i].name = dict.key[i];
259		data_points[i].prefix = NULL;
260		data_points[i].type = DTOP_ULONG;
261		k++;
262	}
263
264	/* Calls dpg constructor, dpg will point to the dp struct */
265	construct_meminfo_file_dpg(data_points, storage);
266
267	free(line_len);
268	dt_free(&data);
269
270	return DTOP_POLL_OK;
271}
272
273/**
274 * @brief Calls dtop_search for "/proc/meminfo" file.
275 */
276void dtop_meminfo_init(void)
277{
278	struct dtop_meminfo_vars *storage = malloc
279			(sizeof(struct dtop_meminfo_vars));
280	storage->line_count = dtop_get_file_line_amount("/proc/meminfo");
281	dtop_meminfo_search(storage);
282}
283