Skip to content

Instantly share code, notes, and snippets.

@phansson
Created August 9, 2015 16:20
Show Gist options
  • Save phansson/efc3dee992ddd1cff41e to your computer and use it in GitHub Desktop.
Save phansson/efc3dee992ddd1cff41e to your computer and use it in GitHub Desktop.
Solaris File Events Notification working example
/* ************************************************************************
* watchdir.c
*
* watchdir uses Solaris' File Event Notification system to monitor
* a directory for changes. It is in this respect similar in use case
* to Linux's inotify-tools.
*
* This method of monitoring a directory is far superior to any
* alternative that would probably use looping and some sort of polling.
*
* watchdir is particularly useful in shell scripts that need to monitor
* a directory for changes.
*
* USAGE:
* Syntax is: watchdir dirname [timeout]
*
* Where
* dirname is the name of the directory to watch (mandatory)
* timeout is the number of seconds to wait for change. If not
* specified the command will wait forever for a change.
*
* watchdir will wait for a change to happen on the monitored directory.
* A change is anything that modifies the directory such as a new file,
* deletion of a file, rename of a file, etc. However file access is
* deliberately not monitored and will not cause the command to stop.
*
* Return value:
* 0 - if the command completes successfully because a change happens on
* the monitored directory.
* 1 - if there was a problem during the initialization, for example
* invalid parameters, memory allocation errors, etc.
* 2 - if the directory was changed abruptly while it was being monitored
* such as if the directory was deleted, renamed or if the file system
* was unmounted.
* 99 - if the command times out before change happens.
*
*
*
* COMPILE:
*
* cc watchdir.c -o watchdir
*
*
*
* LICENSE:
*
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* 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 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.
*
* For more information, please refer to <http://unlicense.org/>
*
* ************************************************************************
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <strings.h>
#include <port.h>
#include <errno.h>
struct fileinfo {
struct file_obj fobj;
int events;
int port;
};
/*
* event handler for file events source.
*/
int process_event(struct fileinfo *finf, int revents)
{
struct file_obj *fobjp = &finf->fobj;
int port = finf->port;
if (revents > 0) {
/*
* If there was an exception we treat it like
* an error.
*/
if (revents & FILE_EXCEPTION) {
return(2);
}
return(0);
}
if (revents == 0) {
/* Don't think we will ever see this */
return(20);
}
return(0);
}
void print_syntax()
{
fprintf(stdout, "\n");
fprintf(stdout, "watchdir will watch a directory for changes using Solaris' File Events Notification.\n");
fprintf(stdout, "This means no polling and zero overhead compared to monitoring a directory in a loop.\n");
fprintf(stdout, "\n");
fprintf(stdout, "Syntax:\n");
fprintf(stdout, " watchdir dirname [timeout]\n");
fprintf(stdout, "\n");
fprintf(stdout, "where:\n");
fprintf(stdout, " dirname is the name of the directory to watch (mandatory)\n");
fprintf(stdout, " timeout is the number of seconds to wait for a change. If not specified the command\n");
fprintf(stdout, " will wait forever for a change on the monitored directory.\n");
fprintf(stdout, "\n");
}
/*
* Tests if the argument is a positive integer.
*/
int is_integer(char *str)
{
int i=0;
for (i = 0; i < strlen(str); i++)
{
if (!isdigit(str[i])) return(0);
}
return(1);
}
int main(int argc, char *argv[])
{
struct fileinfo *finf;
int port;
char *stp;
struct stat sb;
port_event_t pe;
int process_event_result;
struct timespec timeout;
int uses_timeout=0;
if (( argc < 2 ) || (argc > 3)){
if (argc != 1) fprintf(stderr, "Error: Incorrect syntax.\n");
print_syntax();
return(1);
}
if ( argc == 3) {
if (!is_integer(argv[2])) {
fprintf(stderr, "Error: %s is not an integer.\n", argv[2]);
return(1);
} else {
uses_timeout=1;
timeout.tv_sec = atoi(argv[2]);
timeout.tv_nsec = 0;
}
}
if ((port = port_create()) == -1) {
perror("Error: Function port_create() failed.");
}
finf = malloc(sizeof(struct fileinfo));
if (finf == NULL) {
perror("Error: memory alloc");
return(1);
}
if ((finf->fobj.fo_name = strdup(argv[1])) == NULL) {
perror("Error : strdup");
free(finf);
return(1);
}
if ( stat(finf->fobj.fo_name, &sb) == -1) {
perror("Error: Failed to stat file");
fprintf(stderr, "Make sure \"%s\" is an existing and accessible directory.\n", finf->fobj.fo_name);
free(finf->fobj.fo_name);
free(finf);
return(1);
}
if ( ! (sb.st_mode & S_IFDIR) ) {
fprintf(stderr, "Error: \"%s\" is not a directory.\n", finf->fobj.fo_name);
return(1);
}
/*
* Event types to watch.
*/
finf->events = FILE_MODIFIED|FILE_ATTRIB;
finf->port = port;
/*
* Register.
*/
struct file_obj *fobjp = &finf->fobj;
finf->fobj.fo_atime = sb.st_atim;
finf->fobj.fo_mtime = sb.st_mtim;
finf->fobj.fo_ctime = sb.st_ctim;
if (port_associate(port, PORT_SOURCE_FILE, (uintptr_t)fobjp,
finf->events, (void *)finf) == -1) {
/*
* Add error processing as required, file may have been
* deleted/moved.
*/
perror("Error: Failed to register file");
fprintf(stderr, "Failed to register file :%s - errno %d\n",
finf->fobj.fo_name, errno);
free(finf->fobj.fo_name);
free(finf);
return(1);
}
/*
* Listen for events
*/
if (!port_get(port, &pe, (uses_timeout) ? &timeout : NULL)) {
/*
* Can add cases for other sources if this
* port is used to collect events from multiple sources.
*/
switch (pe.portev_source) {
case PORT_SOURCE_FILE:
/* Call file events event handler */
process_event_result = process_event((struct fileinfo *)pe.portev_object, pe.portev_events);
break;
default:
/*
* Don't really see this as happening ... ever !
*/
perror("Error: Event from unexpected source");
return(3);
}
} else {
if (errno == ETIME) {
/* Timeout */
return(99);
} else {
perror("Error while listening for file events ");
return(10);
}
}
/*
* close port, will de-activate all file events watches associated
* with the port.
*/
close(port);
return(process_event_result);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment