Permalink
Browse files

revise driver to use port_control, and add close/1

Use port_control and port_call instead of just port_command to communicate
with the port driver. The port_control and port_call functions include a
command integer argument that makes it easy to send specific commands to
the driver, rather than encoding the commands as atoms in external
format. They also allow for the easy return of simple terms in their
preallocated return buffers. They also allow simple constants to be
returned from the driver for badarg and other general errors. The log
function still uses port_command, but sends an iolist for priority and
message data rather than encoding to external format.

Change open to return a new port, and change the log functions to take the
log port returned from open as its first argument.

Add a new variant of the log function that takes a format string and a list
of format arguments. Drop the timeout argument to log, since it no longer
is meaningful.

Add a close/1 function to close the log.

Modify the determination of the path to the driver shared library, making
it match idiomatic code used for NIFs and drivers in other components.

Add checking of the return values from the ei encoding and decoding
functions in the driver code.

Use driver_alloc and driver_free in the driver code rather than malloc and
free. Also add checks for memory allocation failures.

Use static initialization for the ErlDrvEntry struct. Remove the creation
of driver atom constants that were no longer used.

Add a test to verify that logging on a log that's already been closed
returns an error.
  • Loading branch information...
1 parent b040843 commit ca1e5e181bc3c9c85dfeaf9c23e39034b663c519 @vinoski vinoski committed Dec 7, 2012
Showing with 316 additions and 169 deletions.
  1. +25 −8 README.md
  2. +166 −97 c_src/syslog_drv.c
  3. +125 −64 src/syslog.erl
View
@@ -12,11 +12,11 @@ Installing it
Trying it
---------
-You should have a look at syslog.h
+You should have a look at syslog.h.
-In an other shell :
+In another shell :
- $ tail -f /var/log/sylsog
+ $ tail -f /var/log/syslog
Or, for mac users :
@@ -26,15 +26,16 @@ In erlang shell :
$ erl
> syslog:start().
- > syslog:open("Beuha", [cons, perror, pid], local0).
- > syslog:log(err, "Damned").
+ > {ok,Log} = syslog:open("Beuha", [cons, perror, pid], local0).
+ > syslog:log(Log, err, "Damned").
+ > syslog:log(Log, info, "process count: ~w", [length(processes())]).
API
---
### syslog:open(Ident, Logopt, Facility) ###
-_Ident_ is an arbitrary string
+_Ident_ is an arbitrary string
_Logopt_ is an atom or array of atom, you can use a number if you're brave enough :
* pid
@@ -70,8 +71,13 @@ _Facility_ is an atom :
* local6
* local7
-### syslog:log(Priority, Message) ###
+The `open` call returns either `{ok, Log}` where _Log_ is a syslog handle
+that can be passed to subsequent `log` and `close` calls, or it returns
+`{error, Reason}`.
+### syslog:log(Log, Priority, Message) ###
+
+_Log_ is a syslog handle returned from `open`
_Priority_ can be a number or better, an atom :
* emerg
@@ -83,7 +89,18 @@ _Priority_ can be a number or better, an atom :
* info
* debug
-_Message_ is a String
+_Message_ is a string
+
+### syslog:log(Log, Priority, FormatMsg, FormatArgs) ###
+
+Same as above, but allows for the construction of log messages similar to
+formatting strings via `io_lib:format/2`, where _FormatMsg_ indicates the
+formatting instructions and _FormatArgs_ is a list of arguments to be
+formatted.
+
+### syslog:close(Log) ###
+
+_Log_ is a syslog handle returned from `open`
BUGS
----
View
@@ -31,33 +31,35 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/* for pre-R15 compatibility */
#if ERL_DRV_EXTENDED_MAJOR_VERSION < 2
-#define ErlDrvSizeT int
+typedef int ErlDrvSizeT;
+typedef int ErlDrvSSizeT;
#endif
-/* atoms which are sent to erlang */
-static ErlDrvTermData am_ok;
-static ErlDrvTermData am_value;
-static ErlDrvTermData am_error;
-static ErlDrvTermData am_badver;
-static ErlDrvTermData am_badarg;
-static ErlDrvTermData am_notopen;
+#define DRV_NAME "syslog_drv"
+
+/* the following constants have to match those in syslog.erl */
+#define SYSLOGDRV_OPEN 1
+#define SYSLOGDRV_CLOSE 2
struct syslogdrv {
ErlDrvPort port;
- unsigned char open;
char *ident;
int logopt;
int facility;
+ unsigned char open;
};
typedef struct syslogdrv syslogdrv_t;
-static ErlDrvEntry syslogdrv_driver_entry;
-
-static void encode_error(ei_x_buff *buff, char *error) {
- ei_x_encode_tuple_header(buff, 2);
- ei_x_encode_atom(buff, "error");
- ei_x_encode_atom(buff, error);
+static ErlDrvSSizeT encode_error(char* buf, char* error) {
+ int index = 0;
+ if (ei_encode_version(buf, &index) ||
+ ei_encode_tuple_header(buf, &index, 2) ||
+ ei_encode_atom(buf, &index, "error") ||
+ ei_encode_atom(buf, &index, error)) {
+ return (ErlDrvSSizeT)ERL_DRV_ERROR_GENERAL;
+ }
+ return index+1;
}
static ErlDrvData syslogdrv_start(ErlDrvPort port, char *buf)
@@ -66,117 +68,184 @@ static ErlDrvData syslogdrv_start(ErlDrvPort port, char *buf)
d->port = port;
d->open = 0;
d->ident = NULL;
+ set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
return (ErlDrvData)d;
}
static void syslogdrv_stop(ErlDrvData handle)
{
+ syslogdrv_t* d = (syslogdrv_t*)handle;
+ closelog();
+ if (d->ident) {
+ driver_free(d->ident);
+ }
driver_free((char*)handle);
}
-/* messages from erlang */
-static void syslogdrv_output(ErlDrvData handle, char *buff, ErlDrvSizeT bufflen)
+static void syslogdrv_output(ErlDrvData handle, char *buf, ErlDrvSizeT len)
{
syslogdrv_t* d = (syslogdrv_t*)handle;
- int index = 0, version, arity;
- ei_x_buff result;
- char operation[MAXATOMLEN];
-
- ei_x_new_with_version(&result);
-
- if (ei_decode_version(buff, &index, &version)) {
- encode_error(&result, "badver");
- goto done;
+ /* Incoming data is expected to start with an integer priority encoded
+ as a 4-byte integer in network order, therefore make sure there's at
+ least 5 bytes in the message. */
+ if (d->open && len > 4) {
+ int priority = ntohl(*(int32_t*)buf);
+ buf += 4;
+ /* re-call openlog in case another instance of the port driver
+ * was called in the mean time */
+ openlog(d->ident, d->logopt, d->facility);
+ syslog(priority, "%s", buf);
}
+}
- if (ei_decode_tuple_header(buff, &index, &arity) || ei_decode_atom(buff, &index, operation)) {
- encode_error(&result, "badarg");
- goto done;
+static ErlDrvSSizeT syslogdrv_control(ErlDrvData handle, unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen)
+{
+ syslogdrv_t* d = (syslogdrv_t*)handle;
+ int index = 0, version, arity, type, size;
+
+ if (command != SYSLOGDRV_OPEN) {
+ return (ErlDrvSSizeT)ERL_DRV_ERROR_BADARG;
}
- if (!strcmp("open", operation)) {
- int size, type;
+ if (ei_decode_version(buf, &index, &version)) {
+ return encode_error(*rbuf, "badver");
+ }
+ if (ei_decode_tuple_header(buf, &index, &arity) || arity != 4) {
+ return (ErlDrvSSizeT)ERL_DRV_ERROR_BADARG;
+ }
+ if (ei_get_type(buf, &index, &type, &size)) {
+ return (ErlDrvSSizeT)ERL_DRV_ERROR_BADARG;
+ }
+ if (type == ERL_STRING_EXT) {
+ long logopt, facility, len;
+ ErlDrvBinary* ref = 0;
- if (d->ident) {
- free(d->ident); /* free old ident string */
- d->ident = NULL;
+ syslogdrv_t* nd = (syslogdrv_t*)driver_alloc(sizeof(syslogdrv_t));
+ if (nd == NULL) {
+ return encode_error(*rbuf, "enomem");
}
-
- ei_get_type(buff, &index, &type, &size);
- if (type == ERL_STRING_EXT) {
- long logopt, facility;
-
- d->ident = malloc(size+1);
- ei_decode_string(buff, &index, d->ident);
- if (ei_decode_long(buff, &index, &logopt) || ei_decode_long(buff, &index, &facility)) {
- encode_error(&result, "badarg");
- goto done;
- }
- openlog(d->ident, logopt, facility);
- d->logopt = (int)logopt;
- d->facility = (int)facility;
- d->open = 1;
- ei_x_encode_atom(&result, "ok");
- } else {
- encode_error(&result, "badarg");
+ nd->ident = driver_alloc(size+1);
+ if (nd->ident == NULL) {
+ return encode_error(*rbuf, "enomem");
}
- } else if (!strcmp("log", operation) && d->open) {
- int size, type;
- long priority;
- if (ei_decode_long(buff, &index, &priority)) {
- encode_error(&result, "badarg");
- goto done;
+ if (ei_decode_string(buf, &index, nd->ident)) {
+ driver_free(nd->ident);
+ driver_free(nd);
+ return (ErlDrvSSizeT)ERL_DRV_ERROR_BADARG;
}
- ei_get_type(buff, &index, &type, &size);
- if (type == ERL_STRING_EXT) {
- char *message;
-
- message = malloc(size+1);
- ei_decode_string(buff, &index, message);
- /* re-call openlog in-case another instance of the port driver
- * was called in the mean time */
- openlog(d->ident, d->logopt, d->facility);
- syslog(priority, "%s", message);
- free(message);
- ei_x_encode_atom(&result, "ok");
- } else {
- encode_error(&result, "badarg");
+ if (ei_decode_long(buf, &index, &logopt) ||
+ ei_decode_long(buf, &index, &facility)) {
+ driver_free(nd->ident);
+ driver_free(nd);
+ return (ErlDrvSSizeT)ERL_DRV_ERROR_BADARG;
}
- } else if (!strcmp("log", operation)) { /* NOT open */
- encode_error(&result, "notopen");
+ if (ei_get_type(buf, &index, &type, &size)) {
+ driver_free(nd->ident);
+ driver_free(nd);
+ return (ErlDrvSSizeT)ERL_DRV_ERROR_BADARG;
+ }
+ if (type != ERL_BINARY_EXT) {
+ driver_free(nd->ident);
+ driver_free(nd);
+ return (ErlDrvSSizeT)ERL_DRV_ERROR_BADARG;
+ }
+ ref = driver_alloc_binary(size);
+ if (ref == NULL) {
+ return encode_error(*rbuf, "enomem");
+ }
+ if (ei_decode_binary(buf, &index, ref->orig_bytes, &len)) {
+ driver_free_binary(ref);
+ driver_free(nd->ident);
+ driver_free(nd);
+ return (ErlDrvSSizeT)ERL_DRV_ERROR_BADARG;
+ }
+ nd->logopt = (int)logopt;
+ nd->facility = (int)facility;
+ nd->open = 1;
+ {
+ ErlDrvTermData refdata = TERM_DATA(ref->orig_bytes);
+ ErlDrvPort port = d->port;
+ ErlDrvTermData pid = driver_caller(port);
+ ErlDrvData data = (ErlDrvData)nd;
+ nd->port = driver_create_port(port, pid, DRV_NAME, data);
+ if (nd->port == (ErlDrvPort)-1) {
+ driver_free_binary(ref);
+ driver_free(nd->ident);
+ driver_free(nd);
+ return (ErlDrvSSizeT)ERL_DRV_ERROR_GENERAL;
+ }
+ set_port_control_flags(nd->port, PORT_CONTROL_FLAG_BINARY);
+ ErlDrvTermData term[] = {
+ ERL_DRV_EXT2TERM, refdata, ref->orig_size,
+ ERL_DRV_ATOM, driver_mk_atom("ok"),
+ ERL_DRV_PORT, driver_mk_port(nd->port),
+ ERL_DRV_TUPLE, 2,
+ ERL_DRV_TUPLE, 2,
+ };
+ driver_output_term(port, term, sizeof term/sizeof *term);
+ }
+ driver_free_binary(ref);
+ return 0;
} else {
- encode_error(&result, "badarg");
+ return (ErlDrvSSizeT)ERL_DRV_ERROR_BADARG;
}
+}
- done:
- driver_output(d->port, result.buff, result.index);
- ei_x_free(&result);
+static ErlDrvSSizeT syslogdrv_call(ErlDrvData handle, unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen, unsigned int* flags)
+{
+ syslogdrv_t* d = (syslogdrv_t*)handle;
+ int index = 0;
+
+ if (command != SYSLOGDRV_CLOSE) {
+ return (ErlDrvSSizeT)ERL_DRV_ERROR_BADARG;
+ }
+
+ if (d->ident) {
+ driver_free(d->ident);
+ }
+ d->ident = NULL;
+ d->open = 0;
+ if (ei_encode_version(*rbuf, &index) ||
+ ei_encode_atom(*rbuf, &index, "ok")) {
+ return (ErlDrvSSizeT)ERL_DRV_ERROR_GENERAL;
+ }
+ return index+1;
}
/*
* Initialize and return a driver entry struct
*/
+static ErlDrvEntry syslogdrv_driver_entry = {
+ NULL,
+ syslogdrv_start,
+ syslogdrv_stop,
+ syslogdrv_output,
+ NULL,
+ NULL,
+ DRV_NAME,
+ NULL,
+ NULL,
+ syslogdrv_control,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ syslogdrv_call,
+ NULL,
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+};
DRIVER_INIT(syslogdrv)
{
- am_ok = driver_mk_atom("ok");
- am_value = driver_mk_atom("value");
- am_error = driver_mk_atom("error");
- am_badver = driver_mk_atom("badver");
- am_badarg = driver_mk_atom("badarg");
- am_notopen = driver_mk_atom("notopen");
-
- syslogdrv_driver_entry.init = NULL; /* Not used */
- syslogdrv_driver_entry.start = syslogdrv_start;
- syslogdrv_driver_entry.stop = syslogdrv_stop;
- syslogdrv_driver_entry.output = syslogdrv_output;
- syslogdrv_driver_entry.ready_input = NULL;
- syslogdrv_driver_entry.ready_output = NULL;
- syslogdrv_driver_entry.driver_name = "syslog_drv";
- syslogdrv_driver_entry.finish = NULL;
- syslogdrv_driver_entry.outputv = NULL;
- syslogdrv_driver_entry.extended_marker = ERL_DRV_EXTENDED_MARKER;
- syslogdrv_driver_entry.major_version = ERL_DRV_EXTENDED_MAJOR_VERSION;
- syslogdrv_driver_entry.minor_version = ERL_DRV_EXTENDED_MINOR_VERSION;
return &syslogdrv_driver_entry;
}
Oops, something went wrong.

0 comments on commit ca1e5e1

Please sign in to comment.