Solin Listing 2


1 #include <stdlib.h>
  2 #include <stdio.h>
  3 #include <dirent.h>
  4 #include <unistd.h>
  5 #include <time.h>
  6 #include <string.h>
  7 #include <errno.h>
  8 #include <sys/types.h>
  9 #include <sys/stat.h>
 10 #include <sys/file.h>
 11 
 12 int do_quota_stuff()
 13 {
 14 	/* Create the file-handlers: */
 15 	FILE *file = 0;
 16 	FILE *file2 = 0;
 17   	FILE *file3 = 0;
 18 
 19 	/* Create the directory-hanlders: */
 20 	DIR *dir;
 21 	DIR *dir2;
 22 
 23 	/* The dirent-structure is used to investigate the directories: */
 24 	struct dirent *dir_element;
 25 	struct dirent *dir_element2;
 26 	
 27 	/* The stat-structure is used the get the file-sizes: */
 28 	struct stat buf;
 29 
 30 	/* This variable will hold the current size of a database: */
 31 	double size = 0;
 32 
 33 	/* This variable will hold the maximum allowed database-size: */	
 34 	double quota = 0;
 35 
 36 	/* 
 37 	 * If the maximum allowed database-size is exceeded, this
 38 	 * varaible will hold the number of exceeded megabytes:
 39 	*/
 40 	double exc_mbytes = 0;
 41 	
 42 	/* This variable will hold the current database-directory: */
 43 	char db_dir[512];
 44 
 45 	/* This variable will hold the complete path to the current database-file: */
 46 	char cur_file[512];
 47 
 48 	/* This variable will hold the complete path to the current quota-file: */
 49 	char cur_quota_file[512];
 50 
 51 	/*
 52 	 * If the quota is exceeded for the currently processed database,
 53 	 * this variable will hold the complete path to the file that is
 54 	 * created to indicate that the problem has been reported, and that
 55 	 * it should not be reported again.
 56 	*/	
 57 	char reported_file[512];
 58 
 59 	/*
 60 	 * This variable will hold the complete command that is used to
 61 	 * send an e-mail to the administrator about the occurence or
 62 	 * solving of a quota-problem.
 63 	*/
 64 	char mail_command[1024];
 65 
 66 	/* This variable will be used to calculate the current time: */
 67 	time_t t;
 68 
 69 	/*
 70 	 * When a database has exceeded its quota, this variable will
 71 	 * be set to 1, which will cause a little different output
 72 	 * to the log-file.
 73 	*/ 
 74 	short too_big = 0;
 75 
 76 	/* Open a handle to the data-directory: */
 77 	if( (dir = opendir( "/usr/local/mysql/data" )) == NULL )
 78 	{
 79 		perror( "opendir(/usr/local/mysql/data)" );
 80 	}
 81 
 82 	/* Open the log-file for writing: */
 83 	if( (file2 = fopen( "/usr/local/mysql/logs/quota_log", "a" )) == NULL )
 84 	{
 85 		perror( "fopen(DB_DATA_DIR)" );
 86 	}
 87 
 88 	/* The code generates a timestamp and writes some info to the log-file: */
 89 	time(&t);
 90 	fprintf( file2, "--------------------------------------------------------------\n" );
 91 	fprintf( file2, "Performing quota-check at %s", ctime(&t) );
 92 
 93 	/* This loop scans each directory in /usr/local/mysql/data: */
 94 	while( (dir_element = readdir(dir)) )
 95 	{
 96 		/*
 97 		 * Of course, size should always be 0 when we start calculating
 98 		 * the size of database-directory:
 99 		*/
100 		size = 0;
101  		
102  		/*
103  		 * The database-directory contains a few elements that we want
104  		 * to skip. '.' and '..' is not interesting to us at all. 'mysql'
105  		 * is  database that MySQL uses internally to define user-privileges,
106 		 * so we want to skip that too. 'slackhack.pid' and 'slackhack.err'
107  		 * are also used by MySQL internally, so we want to skip these as well.
108  		 * However, note that the portion in front of the '.' in the two
109 		 * files depends of your hostname. In this, the software was running
110  		 * on a host named 'slackhack'. You need to change this to your
111 		 * own hostname.
112 		*/
113 		if( (!strcmp( dir_element-d_name, ".")) || (!strcmp( dir_element-d_name, "..")) ||
114 			    (!strcmp( dir_element-d_name, "mysql")) || (!strcmp( dir_element-d_name, "slackhack.err"))
115  		    || (!strcmp( dir_element-d_name, "slackhack.pid")) )
116 		{
117 			continue;
118 		}
119 		
120 		/*
121 		 * Store the complete path to the currently processed database-
122 		 * directory in db_dir:
123 		*/
124 		sprintf( db_dir, "%s/%s", "/usr/local/mysql/data", dir_element-d_name );
125 		
126 		/* Open a directory-handle for db_dir: */
127 		if( (dir2 = opendir( db_dir )) == NULL )
128 		{
129 			perror( "opendir(db_dir)" );
130 		}
131 		
132 		/*
133 		 * This loop scans each file in db_dir, adding each file's 
134 		 * size to the variable size.
135 		*/
136 		while( (dir_element2 = readdir(dir2)) )
137 		{
138 			/* We skip '.' and '..': */
139 			if( (!strcmp( dir_element2-d_name, ".")) || (!strcmp( dir_element2-d_name, "..")) )
140 			{
141 				continue;
142 			}
143 			else
144 			{
145 				/*
146 				 * Store the complete path to the currently processed
147 				 * file in the variable cur_file:
148 				*/
149 				sprintf( cur_file, "%s/%s", db_dir, dir_element2-d_name );
150 				
151 				/*
152 				 * Retrive information about the currently processed file: */
153 				stat( cur_file, &buf );
154 	
155 				/*
156 				 * Add the size of the currently processed file to
157 				 * the variable size:
158 				*/
159 				size = (size + buf.st_size);
160 			}
161 		}
162 	
163 		/*
164 		 * Now we're done with this database, so we
165 		 * close the directory-handle:
166 		*/
167 		if( (closedir(dir2)) == -1 )
168 		{
169 			perror( "closedir(dir2)" );
170 		}
171 	
172 		/* We want the size in megabytes, so we do a simple conversio n:*/
173 		size = ((size/1024)/1024);
174 	
175 		/*
176 		 * Store the complete path to the current database's
177 		 * quota-file in cur_quota_file:
178 		*/ 	
179 		sprintf( cur_quota_file, "%s/%s", "/usr/local/mysql/quota", dir_element-d_name );
180 			
181 		/* Open a file-stream to the current quota-file: */	
182 		if( (file = fopen( cur_quota_file, "r" )) == NULL )
183 		{
184 			perror( "fdopen(fd,\"r\")" );
185 		}
186 			
187 		/* Read the value saved in cur_quota_file and store it in quota: */
188 		fscanf( file, "%lf", &quota );
189 		
190 		/* We're done reading from the quota-file, so we close the stream: */
191 		if( (fclose( file )) != 0 )
192 		{
193 			perror( "fclose(file)" );
194 		}
195 	
196 		/*
197 		 * Store the complete path to the file that indicates that a
198 		 * database has been reported as too big in the variable
199 		 * reported_file:
200 		*/
201 		sprintf( reported_file, "%s/%s_reported", "/usr/local/mysql/quota", dir_element-d_name );
202 	
203 		/*
204 		 * Here, we call gen_www_info(), passing it the current
205		 * database-name, size and maximum allowed size. This
206		 * will make gen_www_info() dump some information about
207 		 * the current database in /var/lib/apache/htdocs/quota/$DB_NAME.
208 		*/
209 		if( (gen_www_info( dir_element-d_name, size, quota )) != 0 )
210 		{
211 			/* If there was a problem, report that in the log-file: */
212 			fprintf( file2, "WARNING: Could not create db-info for %s\n", dir_element-d_name );
213 		}
214 			
215 		/* Check to see if the quota has been exceeded: */
216 		if( (size  quota) )
217 		{
218 			/* Store the number of exceeded megabytes in exc_mbytes: */
219 			exc_mbytes = (size - quota);
220 	
221 			/*
222 			 * Write information about the database that has exceeded
223 			 * it's maximum size to the log-file:
224 			*/
225 			fprintf( file2, "%s: WARNING: %.2f/%.2f\n", dir_element-d_name, size, quota );
226 				
227 			/* This will make the output to the log-file a little different: */
228 			too_big = 1;
229 	
230 			/*
231 			 * If the reported-file does not exist, this database
232 			 * has obviously not been reported to the administrator.
233 			 * On the other hand, if the reported-file do exist,
234 			 * the database has been reported, and we avoid doing it
235 			 * again.
236 			*/
237 			if( (fopen(reported_file, "r") == NULL) )
238 			{
239 				/*
240 				 * Generate the complete command for mailing the administrator
241 				 * about this quota-problem:
242 				*/
243 				sprintf( mail_command, "echo \"Hi there!\n\nThis is a report from the MySQL quota daemon. Unfortunately, it looks as the user %s has\nexceeded his/her maximum allowed database-size. The database is %.2fMb, while the maximum\nallowed size for this account is %.2fMb. Maybe we should contact the customer?\n\nUser/Database: %s\nCurrent Status: %.2fMb\nAllowed Size: %.2fMb\n\n\nSincerely,\nYour MySQL Quota Daemon\" | mail -s \"MySQL quota reports regarding %s\" daniel@solin.org", dir_element-d_name, size, quota, dir_element-d_name, size, quota, dir_element-d_name );
244 				
245 				/* We use system() to issue the command: */
246 				system( mail_command );
247
248 				/*
249 				 * By creating the reported-file, we make sure 
250 				 * this problem is reported only once:
251 				*/
252 				file3 = fopen( reported_file, "w" );
253 	
254 				/*
255				 * We output a "1" to the reported-file, but this could					 
256				 * be just about anything. The only thing that the program
257 				 * cares about is if the file exists or not.
258 				*/
259 				fprintf( file3, "1" );
260 	
261 				/* We're done with the reported-file, so we close the stream: */
262 				fclose( file3 );
263 			}
264 		}
265 	
266 		/* 
267 		 * If the quota is not exceeded, but a reported-file exists,
268 		 * the problem has been solved. In other words, the database-
269 		 * size has been decreased by the customer, or the maximum
270 		 * allowed size has been increased by the administrator.
271 		 * In any case, the reported-file can now safely be removed.
272 		*/ 
273 		if( (size < quota) && (file3 = fopen(reported_file, "r")) )
274 		{
275 			/*
276 			 * The file3-stream was opened again above, se we need
277 			 * to close it once more:
278 			*/
279 			fclose( file3 );
280 	
281 			/* Remove the reported-file: */
282 			unlink( reported_file );
283 	
284 			/*
285 			 * This generates the command that will send an e-mail to the
286 			 * administrator, informing him/her that the quota-problems
287 			 * for this database now has been solved, and stores it in the
288 			 * variable mail_command.
289 			*/
290 			sprintf( mail_command, "echo \"Hi again!\n\nI just wanted to let you know that it the quota-problems regarding %s\nseems to be is solved now. See the new data below:\n\nUser/Database: %s\nCurrent Size: %.2fMb\nAllowed Size: %.2fMb\n\n\nAll best,\nMyQuotaD\" | mail -s \"Reports from MyQuotaD regarding %s\" daniel@solin.org", dir_element-d_name, dir_element-d_name, size, quota, dir_element-d_name );
291 				
292 			/* Again, we use system() to issue the command: */
293 			system( mail_command );
294 		}
295 	}
296 	
297 	/*
298 	 * We're done processing database-directories, so we can
299 	 * safely close the directory-handle:
300 	*/
301 	if( (closedir(dir)) == -1 )
302 	{
303 		perror( "closedir(dir)" );
304 	}
305 	
306 	/*
307 	 * If too_big is 1, this indicate that one or more exceeded
308 	 * accounts were found, and that the ending output to the log-
309 	 * file should be a little different, and that the ending output to the log-
310 	 * file should be a little different.
311 	*/
312 	if( too_big == 1 )
313 	{
314 		fprintf( file2, "Result: One or more databases have exceeded their quota!" );
315 	}
316 		
317 	/*
318 	 * If too_big != 1, everything is okay, and we output the usual
319 	 * message that everything seems to be okay.
320 	*/
321 	else
322 	{
323 		fprintf( file2, "Result: Everything seems to be okay!" );
324 	}
325 	
326 	/*
327 	 * We output the ending-line to the log-file, and then closes
328 	 * the file-stream.
329 	*/
330 	fprintf( file2, "\n--------------------------------------------------------------\n\n" );
331 	fclose( file2 );
332 	
333 	/* Everything went just fine, se we return 0: */
334 	return 0;
335 }

