aboutsummaryrefslogblamecommitdiffstats
path: root/src/common.h
blob: 187723ec2eca13f1015ee48563f8433975140633 (plain) (tree)
1
2
   
                                                                     


















                                                                             


                        






                   
         




                                                     


                                                              


                                                                                                                        
       
      



   




































                                                          











                                                                           
                                                                  










                                                        
                         






                                                

                 



                                               

                                   

























                                                                
            

















                                                   













































                                                                                                      
/**
 * Copyright © 2015, 2016  Mattias Andrée <maandree@member.fsf.org>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
#ifndef _DEFAULT_SOURCE
# define _DEFAULT_SOURCE
#endif
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>



#ifndef t
/**
 * Go to `fail` if a statement evaluates to non-zero.
 * 
 * @param  ...  The statement.
 */
# ifndef DEBUG
#  define t(...)  do { if (__VA_ARGS__) goto fail; } while (0)
# else
#  define t(...)  do { if ((__VA_ARGS__) ? (failed__ = #__VA_ARGS__) : 0) { (perror)(failed__); goto fail; } } while (0)
static const char *failed__ = NULL;
#  define perror(_)  ((void)(_))
# endif
#endif



/**
 * A queued job.
 */
struct job {
	/**
	 * The job number.
	 */
	size_t no;

	/**
	 * The number of “argv” elements in `payload`.
	 */
	int argc;

	/**
	 * The clock in which `ts` is measured.
	 */
	clockid_t clk;

	/**
	 * The time when the job shall be executed.
	 */
	struct timespec ts;

	/**
	 * The number of bytes in `payload`.
	 */
	size_t n;

	/**
	 * “argv” followed by “envp”.
	 */
	char payload[];
};



/**
 * Defines the function `void usage(void)`
 * that prints usage information.
 * 
 * @param  synopsis:const char*  The command's synopsis sans command name.
 *                               `NULL` if there are not arguments.
 */
#define USAGE(synopsis)  \
static void  \
usage(void)  \
{  \
	fprintf(stderr, "usage: %s%s%s\n",  \
	        strrchr(argv0, '/') ? (strrchr(argv0, '/') + 1) : argv0,  \
	        synopsis ? " " : "", synopsis ? synopsis : "");  \
	exit(2);  \
}


/**
 * Declares `argv0` and its value to
 * a specified string.
 * 
 * @param  name:sitrng literal  The name of the command.
 */
#define COMMAND(name)  \
const char *argv0 = name;


/**
 * Print usage and exit if there is any argument
 * that is an option.
 */
#define NO_OPTIONS  \
do {  \
	int i;  \
	if (!strcmp(argv[1], "--"))  \
		argv++, argc--;  \
	for (i = 1; i < argc; i++)  \
		if (strchr("-", argv[i][0]))  \
			usage();  \
} while (0)


/**
 * Construct a message from `argv`
 * to send to the daemon.
 */
#define CONSTRUCT_MESSAGE  \
	n = measure_array(argv + 1);  \
	t (n ? !(msg = malloc(n)) : 0);  \
	store_array(msg, argv + 1)


/**
 * Send message to daemon.
 * 
 * @param   cmd:enum command  Command type.
 * @param   n:size_t          The length of the message, 0 if
 *                            `msg` is `NULL` or NUL-terminated.
 * @param   msg:char *        The message to send.
 */
#define SEND(type, n, msg)  \
do {  \
	if (send_command(type, n, msg)) {  \
		t (errno);  \
		free(msg);  \
		return 3;  \
	}  \
} while (0)


/**
 * Exit the process with status indicating success.
 * 
 * Defined the label `fail`.
 * 
 * @param  msg  The message send to the daemon.
 */
#define END(msg)  \
	free(msg);  \
	return 0;  \
fail:  \
	perror(argv0);  \
	free(msg);  \
	return 1;


/**
 * This block of code allows us to compile with DEBUG=valgrind
 * or DEBUG=strace and have all exec:s be wrapped in
 * valgrind --leak-check=full --show-leak-kinds=all or
 * strace, respectively. Very useful for debugging. However,
 * children do not inherit strace, so between forking and
 * exec:ing we do not have strace.
 */
#if 1 && defined(DEBUG)
# if ((DEBUG == 2) || (DEBUG == 3))
#  ifdef __GNUC__
#   pragma GCC diagnostic push
#   pragma GCC diagnostic ignored "-Wdiscarded-qualifiers"
__attribute__((__used__))
#  endif
#  if DEBUG == 2
#   define DEBUGPROG  "strace"
#  else
#   define DEBUGPROG  "valgrind"
#  endif
#  define execl(path, _, ...) (execl)("/usr/bin/" DEBUGPROG, "/usr/bin/" DEBUGPROG, path, __VA_ARGS__)
#  define execve(...) execve_(__VA_ARGS__)
static int
execve_(const char *path, char *const argv[], char *const envp[])
{
	size_t n = 0;
	char **new_argv = NULL;
	int x = (DEBUG - 2) * 2, saved_errno;
	while (argv[n++]);
	t (!(new_argv = malloc((n + 1 + (size_t)x) * sizeof(char *))));
	new_argv[x] = "--show-leak-kinds=all";
	new_argv[1] = "--leak-check=full";
	new_argv[0] = "/usr/bin/" DEBUGPROG;
	new_argv[1 + x] = path;
	memcpy(new_argv + 2 + x, argv + 1, (n - 1) * sizeof(char *));
	(execve)(*new_argv, new_argv, envp);
fail:
	return saved_errno = errno, free(new_argv), errno = saved_errno, -1;
}
#  ifdef __GNUC__
#   pragma GCC diagnostic pop
#  endif
# endif
#endif