#include <freeradius-devel/rad_assert.h>
#include <freeradius-devel/detail.h>
-#include <sys/stat.h>
#include <ctype.h>
#include <fcntl.h>
+#include <sys/stat.h>
#ifdef HAVE_FNMATCH_H
#include <fnmatch.h>
return 0;
}
+/*
+ * Perform a checked write. If the write fails or is not complete, truncate
+ * the file, eliminating the last bytes_accum + current partial write.
+ */
+static int checked_write(REQUEST *request, off_t *bytes_accum, FILE *fp,
+ const char *format, ...)
+{
+ char buf[2048];
+ int buf_used, written;
+ va_list args;
+
+ va_start(args, format);
+ buf_used = vsnprintf(buf, sizeof(buf), format, args);
+
+ written = fputs(buf, fp);
+ if (written > 0) {
+ *bytes_accum += written;
+ }
+ if (written != buf_used) {
+ /*
+ * Don't worry if the truncate fails, since the
+ * detail reader ignores partial entries.
+ */
+ ftruncate(fileno(fp), ftell(fp) - *bytes_accum);
+ fclose(fp);
+
+ radlog_request(L_ERR, 0, request, "Truncated write");
+ return -1;
+ }
+ return written;
+}
+
+
+static int checked_write_vp(REQUEST *request, off_t *bytes_accum, FILE *fp,
+ VALUE_PAIR *vp)
+{
+ if (checked_write(request, bytes_accum, fp, "\t") < 0) {
+ return -1;
+ }
+ vp_print(fp, vp);
+ if (checked_write(request, bytes_accum, fp, "\n") < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
/*
* Do detail, compatible with old accounting
*/
struct stat st;
int locked;
int lock_count;
+ off_t bytes_accum = 0;
struct timeval tv;
VALUE_PAIR *pair;
* feed it through radius_xlat() to expand the
* variables.
*/
- radius_xlat(buffer, sizeof(buffer), inst->detailfile, request, NULL);
+ if (radius_xlat(buffer, sizeof(buffer), inst->detailfile, request, NULL) == 0) {
+ radlog_request(L_ERR, 0, request, "rlm_detail: Failed to expand detail file %s",
+ inst->detailfile);
+ return RLM_MODULE_FAIL;
+ }
RDEBUG2("%s expands to %s", inst->detailfile, buffer);
/*
/*
* Post a timestamp
*/
- fseek(outfp, 0L, SEEK_END);
- radius_xlat(timestamp, sizeof(timestamp), inst->header, request, NULL);
- fprintf(outfp, "%s\n", timestamp);
+ if (fseek(outfp, 0L, SEEK_END) != 0) {
+ radlog_request(L_ERR, 0, request, "rlm_detail: Failed to seek to the end of detail file %s",
+ buffer);
+ fclose(outfp);
+ return RLM_MODULE_FAIL;
+ }
+ if (radius_xlat(timestamp, sizeof(timestamp), inst->header, request, NULL) == 0) {
+ radlog_request(L_ERR, 0, request, "rlm_detail: Unable to expand detail header format %s",
+ inst->header);
+ fclose(outfp);
+ return RLM_MODULE_FAIL;
+ }
+ if (checked_write(request, &bytes_accum, outfp,
+ "%s\n", timestamp) < 0) {
+ return RLM_MODULE_FAIL;
+ }
/*
* Write the information to the file.
*/
if ((packet->code > 0) &&
(packet->code < FR_MAX_PACKET_CODE)) {
- fprintf(outfp, "\tPacket-Type = %s\n",
- fr_packet_codes[packet->code]);
+ if (checked_write(request, &bytes_accum, outfp,
+ "\tPacket-Type = %s\n",
+ fr_packet_codes[packet->code]) == -1) {
+ return RLM_MODULE_FAIL;
+ }
} else {
- fprintf(outfp, "\tPacket-Type = %d\n", packet->code);
+ if (checked_write(request, &bytes_accum, outfp,
+ "\tPacket-Type = %d\n", packet->code) == -1) {
+ return RLM_MODULE_FAIL;
+ }
}
}
break;
}
- fputs("\t", outfp);
- vp_print(outfp, &src_vp);
- fputs("\n", outfp);
- fputs("\t", outfp);
- vp_print(outfp, &dst_vp);
- fputs("\n", outfp);
+ if (checked_write_vp(request, &bytes_accum,
+ outfp, &src_vp) < 0) {
+ return RLM_MODULE_FAIL;
+ }
+ if (checked_write_vp(request, &bytes_accum,
+ outfp, &dst_vp) < 0) {
+ return RLM_MODULE_FAIL;
+ }
src_vp.attribute = PW_PACKET_SRC_PORT;
src_vp.type = PW_TYPE_INTEGER;
dst_vp.type = PW_TYPE_INTEGER;
dst_vp.vp_integer = packet->dst_port;
- fputs("\t", outfp);
- vp_print(outfp, &src_vp);
- fputs("\n", outfp);
- fputs("\t", outfp);
- vp_print(outfp, &dst_vp);
- fputs("\n", outfp);
+ if (checked_write_vp(request, &bytes_accum,
+ outfp, &src_vp) < 0) {
+ return RLM_MODULE_FAIL;
+ }
+ if (checked_write_vp(request, &bytes_accum,
+ outfp, &dst_vp) < 0) {
+ return RLM_MODULE_FAIL;
+ }
}
/* Write each attribute/value to the log file */
/*
* Print all of the attributes.
*/
- fputs("\t", outfp);
- vp_print(outfp, pair);
- fputs("\n", outfp);
+ if (checked_write_vp(request, &bytes_accum,
+ outfp, pair) < 0) {
+ return RLM_MODULE_FAIL;
+ }
}
/*
inet_ntop(request->proxy->dst_ipaddr.af,
&request->proxy->dst_ipaddr.ipaddr,
proxy_buffer, sizeof(proxy_buffer));
- fprintf(outfp, "\tFreeradius-Proxied-To = %s\n",
- proxy_buffer);
- RDEBUG("Freeradius-Proxied-To = %s",
- proxy_buffer);
+ if (checked_write(request, &bytes_accum, outfp,
+ "\tFreeradius-Proxied-To = %s\n",
+ proxy_buffer) < 0) {
+ return RLM_MODULE_FAIL;
+ }
+ RDEBUG("Freeradius-Proxied-To = %s",
+ proxy_buffer);
}
- fprintf(outfp, "\tTimestamp = %ld\n",
- (unsigned long) request->timestamp);
+ if (checked_write(request, &bytes_accum, outfp,
+ "\tTimestamp = %ld\n",
+ (unsigned long) request->timestamp) < 0) {
+ return RLM_MODULE_FAIL;
+ }
/*
* We no longer permit Accounting-Request packets
* with an authenticator of zero.
*/
- fputs("\tRequest-Authenticator = Verified\n", outfp);
+ if (checked_write(request, &bytes_accum, outfp,
+ "\tRequest-Authenticator = Verified\n") < 0) {
+ return RLM_MODULE_FAIL;
+ }
}
- fputs("\n", outfp);
+ if (checked_write(request, &bytes_accum, outfp, "\n") < 0) {
+ return RLM_MODULE_FAIL;
+ }
if (inst->locking) {
fflush(outfp);