Created
August 9, 2015 16:20
-
-
Save phansson/efc3dee992ddd1cff41e to your computer and use it in GitHub Desktop.
Solaris File Events Notification working example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* ************************************************************************ | |
* 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