syslog を Growl で表示してみた

syslog を Growl で通知できれば、便利そうということで、

そのようなアプリケーションを作ってみました。







結果・・・






受信する syslog の量に依りますが、大変なことになりました・・・

#実際には他に目的があるのです!
以下は本当にいい加減なコードで、Growl 通知部分は Growl 本体に付属の growlnotify を参考に(ほとんどコピー)したものですが、

残しておいてみます。

使い方

~% sudo growlsyslog

-d をつけて起動するとデーモンになったりするおまけ機能付きです。

コード

#import
#import
#import "/Volumes/Growl 1.2 SDK/Built-In/GrowlDefines.h"

#include
#include
#include

#include
#include
#include
#include
#include
#include

#include

volatile sig_atomic_t g_gotsig = 0;
int g_foreground = 1;
const char *g_pidfile = "/var/run/growlsyslogd.pid";
static void signal_terminate(int);

int
main(int argc, char **argv) {
struct sigaction sa;
struct addrinfo hints, *res;
struct kevent kev;
struct timespec ts;
int ch, kq, sockfd, ret, val = 0;
ssize_t rsize;
pid_t pid;
char *progname;
FILE *pidfp;
char buf[1024];
struct sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

if ( (progname = strchr(*argv, '/')) == NULL)
progname = *argv;
else
progname++;

while ( (ch = getopt(argc, argv, "dp:")) != -1) {
switch (ch) {
case 'd':
g_foreground--;
break;
case 'p':
g_pidfile = optarg;
default:
return (EX_USAGE);
/* NOTREACHED */
}
}
argc -= optind;
argv += optind;

if (argc != 0) {
return (EX_USAGE);
/* NOTREACHED */
}

pid = getpid();
if ( (pidfp = fopen(g_pidfile, "w")) != NULL) {
(void) fprintf(pidfp, "%d\n", pid);
(void) fclose(pidfp);
}

(void) memset(&sa, 0, sizeof(sa));
(void) sigaction(SIGINT, NULL, &sa);
sa.sa_handler = signal_terminate;
sa.sa_flags = SA_NODEFER;
(void) sigemptyset(&sa.sa_mask);
(void) sigaddset(&sa.sa_mask, SIGINT);
(void) sigaction(SIGINT, &sa, NULL);

(void) sigaction(SIGTERM, NULL, &sa);
sa.sa_handler = signal_terminate;
sa.sa_flags = SA_NODEFER;
(void) sigemptyset(&sa.sa_mask);
(void) sigaddset(&sa.sa_mask, SIGTERM);
(void) sigaction(SIGTERM, &sa, NULL);

(void) memset(&hints, 0x00, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;

if (getaddrinfo(NULL, "514", &hints, &res) != 0)
return (EXIT_FAILURE);
if ( (sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1)
return (EXIT_FAILURE);
val = 1;
if (bind(sockfd, res->ai_addr, res->ai_addrlen) == -1)
return (EXIT_FAILURE);
freeaddrinfo(res);

CFStringRef appName = CFStringCreateWithCString(kCFAllocatorDefault, "growlsyslog", kCFStringEncodingUTF8);
NSSize imageSize = NSMakeSize(128,128);
NSImage *image = [[[NSImage alloc] initWithSize:imageSize] autorelease];
CFDataRef appIcon = (CFDataRef)[image TIFFRepresentation];
CFArrayRef defaultAndAllNotifications = CFArrayCreate(kCFAllocatorDefault, (const void **)&appName, 1, &kCFTypeArrayCallBacks);
CFTypeRef registerKeys[4] = {GROWL_APP_NAME,
GROWL_NOTIFICATIONS_ALL,
GROWL_NOTIFICATIONS_DEFAULT,
GROWL_APP_ICON};
CFTypeRef registerValues[4] = {appName,
defaultAndAllNotifications,
defaultAndAllNotifications,
appIcon};
CFDictionaryRef registerInfo = CFDictionaryCreate(kCFAllocatorDefault,
registerKeys,
registerValues,
4,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFRelease(defaultAndAllNotifications);

val = 0;
CFStringRef title = CFStringCreateWithCString(kCFAllocatorDefault, "SYSLOG", kCFStringEncodingUTF8);
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
CFStringRef clickContext = CFUUIDCreateString(kCFAllocatorDefault, uuid);
CFNumberRef priority = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &val);
CFBooleanRef sticky = kCFBooleanFalse;

CFNotificationCenterRef distCenter = CFNotificationCenterGetDistributedCenter();

ts.tv_sec = 0;
ts.tv_nsec = 100 * 1000 * 1000;

if ( (kq = kqueue()) == -1)
return (EXIT_FAILURE);

EV_SET(&kev, sockfd, EVFILT_READ, EV_ADD, 0, 0, NULL);
if (kevent(kq, &kev, 1, NULL, 0, NULL) == -1)
return (EXIT_FAILURE);

while (g_gotsig == 0) {
if ( (ret = kevent(kq, NULL, 0, &kev, 1, &ts)) == -1) {
if (errno != EINTR) {
break;
} else {
continue;
}
} else if (ret == 0) {
continue;
} else {
if ( (rsize = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *) &addr, &addr_len)) == -1) {
if (errno != EINTR) {
break;
} else {
continue;
}
} else {
buf[rsize - 1] = '\0';
CFStringRef message = CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingUTF8);

CFMutableDictionaryRef notificationInfo = CFDictionaryCreateMutable(kCFAllocatorDefault,
9,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_NAME, appName);
CFDictionarySetValue(notificationInfo, GROWL_APP_NAME, appName);
CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_TITLE, title);
CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_DESCRIPTION, message);
CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_PRIORITY, priority);
CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_STICKY, sticky);
CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_ICON, appIcon);
CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_CLICK_CONTEXT, clickContext);

CFNotificationCenterPostNotificationWithOptions(distCenter, (CFStringRef)GROWL_APP_REGISTRATION, NULL, registerInfo, kCFNotificationPostToAllSessions);
CFNotificationCenterPostNotificationWithOptions(distCenter, (CFStringRef)GROWL_NOTIFICATION, NULL, notificationInfo, kCFNotificationPostToAllSessions);

CFRelease(notificationInfo);

CFRelease(message);
}
}
}

(void) close(kq);
(void) close(sockfd);

CFRelease(priority);
CFRelease(title);
CFRelease(clickContext);
CFRelease(appName);
CFRelease(appIcon);

[pool release];

return (EXIT_SUCCESS);
}

static void
signal_terminate(int signal_number)
{
g_gotsig = (sig_atomic_t) signal_number;
}