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_gen_poll.c
32 * @brief Contains functions which add ability to scan directories for data points.
33 *
34 * Contains functions that search through a directory and create dpg's for any
35 * important data values found which can then be polled for data collection.
36 */
37
38#include <unistd.h>
39#include <stdio.h>
40#include <dirent.h>
41#include <string.h>
42#include <stdlib.h>
43#include <errno.h>
44#include <sys/stat.h>
45#include <sys/types.h>
46
47#include "datatop_interface.h"
48#include "datatop_fileops.h"
49#include "datatop_str.h"
50#include "datatop_gen_poll.h"
51
52#define DTOP_GEN_SIZE 8192
53#define DTOP_GEN_LINE (DTOP_GEN_SIZE>>2)
54
55/**
56 * @brief Searches a file to find the number of data values it contains.
57 *
58 * @param dpg The struct which contains the file to search.
59 * @return Number of datapoints found in the file.
60 */
61static int get_number_of_values(struct dtop_data_point_gatherer *dpg)
62{
63	char *data;
64	int read;
65	char line[DTOP_GEN_LINE];
66	int line_len;
67	int i, num;
68
69	read = dt_read_file(dpg->file, &data, DTOP_GEN_SIZE);
70	line_len = dt_read_line(line, DTOP_GEN_LINE, data, DTOP_GEN_SIZE, 0);
71
72	if (read == 0) {
73		return 0;
74	}
75
76	if (line_len < 1) {
77		dt_free(&data);
78		return 0;
79	}
80
81	num = 1;
82	for (i = 0; i < line_len; i++) {
83		if ((line[i] == ' ' || line[i] == ','
84		    || line[i] == '	') &&line[i+1] != 0)
85			num++;
86	}
87
88	dt_free(&data);
89	return num;
90}
91
92/**
93 * @brief Stores the data collected from a dpg that was constructed during dtop_search.
94 *
95 * @param dpg Struct that polled data is added to.
96 * @return DTOP_POLL_IO_ERR - Poll of dpg unsuccessful.
97 * @return DTOP_POLL_OK - Poll of dpg successful.
98 */
99int dtop_gen_poll(struct dtop_data_point_gatherer *dpg)
100{
101	char *data;
102	int read;
103	char line[DTOP_GEN_LINE];
104	int line_len;
105	struct dt_procdict dict;
106	int i;
107
108	read = dt_read_file(dpg->file, &data, DTOP_GEN_SIZE);
109	line_len = dt_read_line(line, DTOP_GEN_LINE, data, DTOP_GEN_SIZE, 0);
110
111	if (read == 0 || data == 0)
112		return DTOP_POLL_IO_ERR;
113
114	dt_single_line_parse(line, line_len, &dict);
115
116	for (i = 0; i < dpg->data_points_len; i++) {
117		if (dict.val[i][0] == '-')
118			dpg->data_points[i].type = DTOP_LONG;
119		dtop_store_dp(&(dpg->data_points[i]), dict.val[i]);
120	}
121
122	dt_free(&data);
123	return DTOP_POLL_OK;
124}
125
126/**
127 * @brief Frees dynamically allocated dpg's.
128 *
129 * Frees the memory of dpg variables and the dpg for all dynamically allocated
130 * dpgs.
131 *
132 * @param dpg Dpg to deconstruct and deallocate memory for.
133 */
134static void dtop_gen_dpg_deconstructor(struct dtop_data_point_gatherer *dpset)
135{
136	int i;
137	for (i = 0; i < dpset->data_points_len; i++)
138		free(dpset->data_points[i].name);
139	free(dpset->data_points);
140	free(dpset->file);
141	free(dpset->prefix);
142	free(dpset);
143}
144
145/**
146 * @brief Creates a dpg and all necessary dp's corresponding to it.
147 *
148 * Dynamically allocates memory for dpg and dp structs which are then
149 * created and added to a linked_list of dpgs through the dtop_register
150 * function.
151 *
152 * @param dir Directory which file is located in, assigned to the dpg prefix.
153 * @param name Name of file that dpg represents, assigned to a dp name.
154 */
155static void dpg_construction(char *dir, char *name)
156{
157	int num, i;
158	int both_len = strlen(dir) + strlen(name) + 1;
159	char *both = malloc(both_len);
160	char *maindir;
161	struct dtop_data_point_gatherer *dpg = malloc
162	       (sizeof(struct dtop_data_point_gatherer));
163	strlcpy(both, dir, both_len);
164	strlcat(both, name, both_len);
165	maindir = malloc(strlen(dir) + 1);
166	strlcpy(maindir, dir, strlen(dir) + 1);
167
168	dpg->prefix = maindir;
169	dpg->file = both;
170	dpg->poll = dtop_gen_poll;
171	dpg->deconstruct = dtop_gen_dpg_deconstructor;
172	num = get_number_of_values(dpg);
173
174	if (num != 0) {
175		struct dtop_data_point *dp = malloc
176		       (num * sizeof(struct dtop_data_point));
177		for (i = 0; i < num; i++) {
178			if (num == 1) {
179				dp[i].name = malloc(strlen(name) + 1);
180				strlcpy(dp[i].name, name, strlen(name) + 1);
181			} else {
182				char *add = malloc(7 * sizeof(char));
183				char *newname;
184				int nn_len, dpn_len;
185				snprintf(add, 7 * sizeof(char), "[%d]:", i);
186				nn_len = strlen(name) + strlen(add) + 1;
187				newname = malloc(nn_len);
188				strlcpy(newname, name, nn_len);
189				strlcat(newname, add, nn_len);
190				dpn_len = strlen(newname) + 1;
191				dp[i].name = malloc(dpn_len);
192				strlcpy(dp[i].name, newname, dpn_len);
193				free(add);
194				free(newname);
195			}
196			dp[i].prefix = NULL;
197			dp[i].type = DTOP_ULONG;
198			dp[i].skip = DO_NOT_SKIP;
199			dp[i].initial_data_populated = NOT_POPULATED;
200		}
201
202		dpg->data_points = dp;
203		dpg->data_points_len = num;
204
205		dtop_register(dpg);
206	} else {
207		free(dpg->prefix);
208		free(dpg->file);
209		free(dpg);
210	}
211}
212
213/**
214 * @brief Scans a directory for all important datapoints to be collected.
215 *
216 * Recursively scans a directory and locates all files which data will be
217 * collected from.
218 *
219 * @param dir Directory to search.
220 */
221static int dtop_search(char *dir)
222{
223	DIR *dp;
224	struct dirent *entry;
225	struct stat s;
226	char cwd[1024];
227
228	if (!getcwd(cwd, sizeof(cwd))) {
229		fprintf(stderr, "Failed to get current working dir\n");
230		return -1;
231	}
232
233	dp = opendir(dir);
234	if (dp == NULL) {
235		fprintf(stderr, "err=%d: %s\n", errno, strerror(errno));
236		fprintf(stderr, "Cannot open directory: %s\n", dir);
237		return DIR_FAILURE;
238	}
239
240	chdir(dir);
241
242	while ((entry = readdir(dp))) {
243		if (stat(entry->d_name, &s)) {
244			printf("stat err=%d: %s\n", errno, strerror(errno));
245			return DIR_FAILURE;
246		}
247
248		if (strcmp(".", entry->d_name) != 0 &&
249		   strcmp("..", entry->d_name) != 0 &&
250		   S_ISREG(s.st_mode)) {
251
252			dpg_construction(dir, entry->d_name);
253
254		} else if (strcmp(".", entry->d_name) != 0 &&
255			strcmp("..", entry->d_name) != 0 &&
256			S_ISDIR(s.st_mode)) {
257			int nd_len = strlen(dir) + strlen(entry->d_name) + 2;
258			char *newdir = malloc(nd_len);
259			strlcpy(newdir, dir, nd_len);
260			strlcat(newdir, entry->d_name, nd_len);
261			strlcat(newdir, "/", nd_len);
262			dtop_search(newdir);
263			free(newdir);
264		}
265	}
266
267	closedir(dp);
268	chdir(cwd);
269	return DIR_SUCCESS;
270}
271
272/**
273 * @brief Calls dtop_search for any specified directory.
274 *
275 * @param dir Directory to search.
276 */
277void dtop_gen_init(char *dir)
278{
279	dtop_search(dir);
280}
281