/*-
* Copyright (C) 2005-2007 Nick Withers. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "downtime.h"
#include <ctype.h>
#include <errno.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#ifdef BSD
#include <sysexits.h>
#endif
char
add_to_unique_string_array(char ***string_array, size_t *array_length, char *string_to_add)
{
if ((*array_length == 0 || !is_in_string_array(*string_array, *array_length, string_to_add)) && add_to_string_array(string_array, array_length, string_to_add))
return (TRUE);
return (FALSE);
}
/*
* Adds the string_to_add to a string array pointed to by string_array of
* length *array_length.
*
* NOTE:
* - *string_array holds a pointer to malloc()-ed space and should be freed
* by the calling function when it is no longer required.
* - add_to_string_array() attempts to free() the memory pointed to by
* *string_array when *array_length > 0. For this reason, if the string
* array pointed to by *string_array has been touched by anything other
* than add_to_string_array(), please ensure that the space it points to was
* obtained through malloc()
*/
char
add_to_string_array(char ***string_array, size_t *array_length, char *string_to_add)
{
return (array_add((void **) string_array, array_length, (void *) &string_to_add, sizeof(string_to_add)));
}
/*
* Adds *add_me to an array of length *length with elements of elem_size pointed to by array
*
* NOTE: The memory referenced by *array should be free()d when no longer required
*/
char
array_add(void **array, size_t *length, const void *add_me, size_t elem_size)
{
if (*length == 0)
*array = NULL;
if ((*array = realloc(*array, (*length + 1) * elem_size)) == NULL)
return (FALSE);
memmove((*((char **) array) + *length * elem_size), add_me, elem_size);
(*length)++;
return (TRUE);
}
/*
*
* Appends the character append_me to the malloc()-ed original_string
* string, which may either be NULL-terminated or be a pointer to NULL
*
* NOTE:
* - *original_string holds a pointer to malloc()-ed space and should be freed
* by the calling function when it is no longer required.
* - cat_strchar() attempts to free() the memory pointed to by
* original_string when it doesn't point to NULL. For this reason, if the string
* array pointed to by original_string has been touched by anything other
* than add_to_string_array(), please ensure that the space it points to was
* obtained through malloc()
*
*/
char
cat_strchar(char **original_string, const char append_me)
{
size_t string_length;
if (*original_string == NULL)
string_length = 0;
else
string_length = strlen(*original_string);
if (!array_add((void **) original_string, &string_length, &append_me, sizeof (append_me)) || !array_add((void **) original_string, &string_length, "", sizeof (char))) /* Append the NULL character, too */
return (FALSE);
return (TRUE);
}
/*
*
* Appends the string append_me to original_string in a new, malloc()-ed, string
*
* NOTE:
* - new_string holds a pointer to malloc()ed space and should be freed
* by the calling function when it is no longer required.
* - original_string is NOT free()d in cat_strings. Therefore, code like
* string = cat_strings(string, "-bam");
* where "string" is a pointer to malloc()ed space, causes a memork leak
* - trying to append a blank string to a blank string is not treated as a
* special case: You'll get a blank string in malloc()ed space back.
* This is so that we can fulfill our promise to return a pointer to
* malloc()ed space - after all, there will be free()s performed on the
* return values of this function!
*/
char *
cat_strings(const char *original_string, const char *append_me)
{
char *new_string;
size_t string_length;
string_length = ((original_string == NULL) ? 0 : strlen(original_string)) + ((append_me == NULL) ? 0 : strlen(append_me));
if ((new_string = (char *) malloc(sizeof(char) * (string_length + 1))) == NULL)
return (NULL);
if (original_string != NULL)
{
strcpy(new_string, original_string);
strcat(new_string, append_me);
}
else
strcpy(new_string, append_me);
*(new_string + string_length) = '\0'; /* NULL terminate the string */
return (new_string);
}
char *
find_executable_in_path(char *filename)
{
size_t i, path_len, path_elements, path_offset;
char **paths, *path, *location;
if ((path = cat_strings(geteuid() == getuid() ? getenv("PATH") : NULL, _PATH_STDPATH)) == NULL) /* Check if the program is running setuid as another user. If it is, don't add user path environment variables for security reasons */
show_error_dialog(EX_OSERR, "Couldn't store environment PATH. Error: \"%s\" (%d)", strerror(errno), errno);
path_len = strlen(path);
for (i = 0; i < path_len; i++) /* Replace PATH_SEPARATOR characters in path with NUL character */
{
if (*(path + i) == PATH_SEPARATOR)
*(path + i) = '\0';
}
path_offset = 0;
path_elements = 0;
while (path_offset < path_len) /* Add each substring of path to paths */
{
char *new_path_element = malloc((strlen(path + path_offset) + 1) * sizeof (char)); /* Copy the string to be added into a freshly malloc()ed string. This allows us to free() path, but note that we must now free() each element in paths */
strcpy(new_path_element, (path + path_offset));
if (!add_to_unique_string_array(&paths, &path_elements, new_path_element)) /* If new_path_element isn't added to the array, free() it */
free(new_path_element);
path_offset += strlen(path + path_offset) + 1; /* Increment path_offset so that (path + path_offset) will point past the terminating NUL character of the current string on the next run (if any) */
}
free(path);
for (i = 0; i < path_elements; i++) /* Iterate through each element of paths */
{
location = malloc(sizeof (char *) * (strlen(*(paths + i)) + strlen(filename) + 2)); /* + 2: 1 for '/', 1 for '\0' */
strcpy(location, *(paths + i));
strcat(location, "/");
strcat(location, filename);
if (access(location, F_OK) == 0 && access(location, X_OK) == 0)
{
size_t j;
for (j = 0; j < path_elements; j++)
free(*(paths + j));
free(paths);
return (location);
}
free(location);
}
for (i = 0; i < path_elements; i++)
free(*(paths + i));
free(paths);
return (NULL);
}
char *
get_filename_from_path(const char *path)
{
char *filename;
if ((filename = strrchr(path, DIR_SEPARATOR)) != NULL)
return (++filename);
else
return (NULL);
}
char *
get_shutdown_time_left_string(shutdown_t *shutdown)
{
#define SECONDS_IN_DAY 86400 /* (60 seconds / minute) * (60 minutes / hour) * (24 hours / day) */
struct tm *time_to_shutdown;
time_t seconds_to_shutdown;
char *str_time_to_shutdown;
seconds_to_shutdown = shutdown->shutdown_time - time(NULL);
time_to_shutdown = gmtime(&seconds_to_shutdown);
if ((time_to_shutdown->tm_year > 70) || (time_to_shutdown->tm_mon > 0) || (time_to_shutdown->tm_mday > 1))
pasprintf(&str_time_to_shutdown, "%ld day%s, %d:%2.2d:%2.2d", (long) (seconds_to_shutdown / SECONDS_IN_DAY), (long) (seconds_to_shutdown / SECONDS_IN_DAY) > 1 ? "s" : "", time_to_shutdown->tm_hour, time_to_shutdown->tm_min, time_to_shutdown->tm_sec);
else if (time_to_shutdown->tm_hour > 0)
pasprintf(&str_time_to_shutdown, "%d:%2.2d:%2.2d", time_to_shutdown->tm_hour, time_to_shutdown->tm_min, time_to_shutdown->tm_sec);
else
pasprintf(&str_time_to_shutdown, "%d:%2.2d", time_to_shutdown->tm_min, time_to_shutdown->tm_sec);
if (str_time_to_shutdown == NULL)
show_error_dialog(EX_OSERR, "pasprintf() returned -1 in get_shutdown_time_left_string(). Error: \"%s\" (%d)", strerror(errno), errno);
return (str_time_to_shutdown);
#undef SECONDS_IN_DAY
}
void
init_user_specs(user_specs_t *specs)
{
bzero(specs, sizeof (*specs));
specs->timeout = DEFAULT_TIMEOUT;
specs->timeout_unit = DEFAULT_TIMEOUT_UNIT;
specs->shutdown_type = DEFAULT_SHUTDOWN_TYPE;
specs->remember = DEFAULT_REMEMBER;
}
char
is_in_string_array(char **string_array, size_t array_length, char *string_to_add)
{
size_t i;
for (i = 0; i < array_length; i++)
{
if (strcmp(string_to_add, *(string_array + i)) == 0)
return (TRUE);
}
return (FALSE);
}
/* Returns the maximum number of minutes until a scheduled shutdown. So, for instance, if a shutdown was to occur in 6 minutes and 25 seconds, this would return 7 */
long int
max_minutes_to_shutdown(shutdown_t *shutdown)
{
double minutes;
minutes = ((double) (shutdown->shutdown_time - time(NULL))) / 60;
if (minutes > (long int) minutes) /* Round minutes up */
minutes = (long int) minutes + 1;
return ((long int) minutes);
}
/*
* Portable asprintf(). Should behave identically to asprintf() on FreeBSD 6.0-RELEASE
*/
int
pasprintf(char **ret, const char *fmt, ...)
{
va_list ap;
size_t length;
va_start(ap, fmt);
length = vsnprintf(NULL, 0, fmt, ap);
if ((*ret = malloc((length + 1) * sizeof (char))) == NULL) /* + 1 for the trailing NUL character */
{
va_end(ap);
ret = NULL; /* As with pasprintf() on FreeBSD, set ret = NULL in case of error*/
return (-1); /* As with pasprintf() on FreeBSD, return -1 in case of error. errno will already have been set by malloc() */
}
vsnprintf(*ret, length + 1, fmt, ap);
va_end(ap);
return (length);
}
/*
* Portable vasprintf(). Should behave identically to vasprintf() on FreeBSD 6.0-RELEASE
*/
int
pvasprintf(char **ret, const char *fmt, va_list ap)
{
size_t length;
length = vsnprintf(NULL, 0, fmt, ap);
if ((*ret = malloc((length + 1) * sizeof (char))) == NULL) /* + 1 for the trailing NUL character */
{
ret = NULL; /* As with pasprintf() on FreeBSD, set ret = NULL in case of error*/
return (-1); /* As with pasprintf() on FreeBSD, return -1 in case of error. errno will already have been set by malloc() */
}
vsnprintf(*ret, length + 1, fmt, ap);
return (length);
}
/*
*
* Trim white-space characters from the beginning and the end of a
* NULL-terminated string. A pointer to malloc()ed space containing the trimmed
* string is returned which should be freed by the calling function when no
* longer required
*
*/
char *
trim_white(const char *string)
{
char *trimmed, *temp;
size_t i;
while ((*string == ' ' || *string == '\t' || *string == '\n') && *string != '\0')
string++;
if ((temp = (char *) malloc(sizeof (char) * (strlen(string) + 1))) == NULL)
show_error_dialog(EX_OSERR, "malloc() returned NULL in trim_white(). Error: \"%s\" (%d)", strerror(errno), errno);
strcpy(temp, string);
for (i = strlen(temp); *(temp + i) == '\0' || isspace(*(temp + i)); i--)
;
if (i < strlen(temp))
*(temp + i + 1) = '\0';
if ((trimmed = (char *) malloc(sizeof (char) * (strlen(temp) + 1))) == NULL)
show_error_dialog(EX_OSERR, "malloc() returned NULL in trim_white(). Error: \"%s\" (%d)", strerror(errno), errno);
strcpy(trimmed, temp);
free(temp);
return (trimmed);
}
syntax highlighted by Code2HTML, v. 0.9.1