static rlm_rcode_t CC_HINT(nonnull) mod_do_linelog(void *instance, REQUEST *request)
{
    int fd = -1;
    rlm_linelog_t *inst = (rlm_linelog_t*) instance;
    char const *value = inst->line;

#ifdef HAVE_GRP_H
    gid_t gid;
    char *endptr;
#endif
    char path[2048];
    char line[4096];

    line[0] = '\0';

    if (inst->reference) {
        CONF_ITEM *ci;
        CONF_PAIR *cp;

        if (radius_xlat(line + 1, sizeof(line) - 1, request, inst->reference, linelog_escape_func, NULL) < 0) {
            return RLM_MODULE_FAIL;
        }

        line[0] = '.';	/* force to be in current section */

        /*
         *	Don't allow it to go back up
         */
        if (line[1] == '.') goto do_log;

        ci = cf_reference_item(NULL, inst->cs, line);
        if (!ci) {
            RDEBUG2("No such entry \"%s\"", line);
            return RLM_MODULE_NOOP;
        }

        if (!cf_item_is_pair(ci)) {
            RDEBUG2("Entry \"%s\" is not a variable assignment ", line);
            goto do_log;
        }

        cp = cf_item_to_pair(ci);
        value = cf_pair_value(cp);
        if (!value) {
            RWDEBUG2("Entry \"%s\" has no value", line);
            return RLM_MODULE_OK;
        }

        /*
         *	Value exists, but is empty.  Don't log anything.
         */
        if (!*value) return RLM_MODULE_OK;
    }

do_log:
    /*
     *	FIXME: Check length.
     */
    if (radius_xlat(line, sizeof(line) - 1, request, value, linelog_escape_func, NULL) < 0) {
        return RLM_MODULE_FAIL;
    }

#ifdef HAVE_SYSLOG_H
    if (strcmp(inst->filename, "syslog") == 0) {
        syslog(inst->syslog_priority, "%s", line);
        return RLM_MODULE_OK;
    }
#endif

    /*
     *	We're using a real filename now.
     */
    if (radius_xlat(path, sizeof(path), request, inst->filename, inst->escape_func, NULL) < 0) {
        return RLM_MODULE_FAIL;
    }

    fd = exfile_open(inst->ef, path, inst->permissions, true);
    if (fd < 0) {
        ERROR("rlm_linelog: Failed to open %s: %s", path, fr_syserror(errno));
        return RLM_MODULE_FAIL;
    }

    if (inst->group != NULL) {
        gid = strtol(inst->group, &endptr, 10);
        if (*endptr != '\0') {
            if (rad_getgid(request, &gid, inst->group) < 0) {
                RDEBUG2("Unable to find system group \"%s\"", inst->group);
                goto skip_group;
            }
        }

        if (chown(path, -1, gid) == -1) {
            RDEBUG2("Unable to change system group of \"%s\"", path);
        }
    }

skip_group:
    strcat(line, "\n");

    if (write(fd, line, strlen(line)) < 0) {
        exfile_close(inst->ef, fd);
        ERROR("rlm_linelog: Failed writing: %s", fr_syserror(errno));
        return RLM_MODULE_FAIL;
    }

    exfile_close(inst->ef, fd);
    return RLM_MODULE_OK;
}
/*
 *	Instantiate the module.
 */
static int mod_instantiate(CONF_SECTION *conf, void *instance)
{
	linelog_instance_t	*inst = instance;
	char			prefix[100];

	/*
	 *	Escape filenames only if asked.
	 */
	if (inst->file.escape) {
		inst->file.escape_func = rad_filename_escape;
	} else {
		inst->file.escape_func = rad_filename_make_safe;
	}

	inst->log_dst = fr_str2int(linelog_dst_table, inst->log_dst_str, LINELOG_DST_INVALID);
	if (inst->log_dst == LINELOG_DST_INVALID) {
		cf_log_err_cs(conf, "Invalid log destination \"%s\"", inst->log_dst_str);
		return -1;
	}

	if (!inst->log_src && !inst->log_ref) {
		cf_log_err_cs(conf, "Must specify a log format, or reference");
		return -1;
	}

	inst->name = cf_section_name2(conf);
	if (!inst->name) inst->name = cf_section_name1(conf);

	snprintf(prefix, sizeof(prefix), "rlm_linelog (%s)", inst->name);

	/*
	 *	Setup the logging destination
	 */
	switch (inst->log_dst) {
	case LINELOG_DST_FILE:
	{
		if (!inst->file.name) {
			cf_log_err_cs(conf, "No value provided for 'filename'");
			return -1;
		}

		inst->file.ef = exfile_init(inst, 64, 30, true);
		if (!inst->file.ef) {
			cf_log_err_cs(conf, "Failed creating log file context");
			return -1;
		}

		if (inst->file.group_str) {
			char *endptr;

			inst->file.group = strtol(inst->file.group_str, &endptr, 10);
			if (*endptr != '\0') {
				if (rad_getgid(inst, &(inst->file.group), inst->file.group_str) < 0) {
					cf_log_err_cs(conf, "Unable to find system group \"%s\"",
						      inst->file.group_str);
					return -1;
				}
			}
		}
	}
		break;

	case LINELOG_DST_SYSLOG:
	{
		int num;

#ifndef HAVE_SYSLOG_H
		cf_log_err_cs(conf, "Syslog output is not supported on this system");
		return -1;
#else
		if (inst->syslog.facility) {
			num = fr_str2int(syslog_facility_table, inst->syslog.facility, -1);
			if (num < 0) {
				cf_log_err_cs(conf, "Invalid syslog facility \"%s\"", inst->syslog.facility);
				return -1;
			}
			inst->syslog.priority |= num;
		}

		num = fr_str2int(syslog_severity_table, inst->syslog.severity, -1);
		if (num < 0) {
			cf_log_err_cs(conf, "Invalid syslog severity \"%s\"", inst->syslog.severity);
			return -1;
		}
		inst->syslog.priority |= num;
#endif
	}
		break;

	case LINELOG_DST_UNIX:
#ifndef HAVE_SYS_UN_H
		cf_log_err_cs(conf, "Unix sockets are not supported on this sytem");
		return -1;
#else
		inst->pool = fr_connection_pool_module_init(cf_section_sub_find(conf, "unix"),
							    inst, mod_conn_create, NULL, prefix);
		if (!inst->pool) return -1;
#endif
		break;

	case LINELOG_DST_UDP:
		inst->pool = fr_connection_pool_module_init(cf_section_sub_find(conf, "udp"),
							    inst, mod_conn_create, NULL, prefix);
		if (!inst->pool) return -1;
		break;

	case LINELOG_DST_TCP:
		inst->pool = fr_connection_pool_module_init(cf_section_sub_find(conf, "tcp"),
							    inst, mod_conn_create, NULL, prefix);
		if (!inst->pool) return -1;
		break;

	case LINELOG_DST_INVALID:
		rad_assert(0);
		break;
	}

	inst->delimiter_len = talloc_array_length(inst->delimiter) - 1;
	inst->cs = conf;

	return 0;
}
/*
 *	Do detail, compatible with old accounting
 */
static rlm_rcode_t CC_HINT(nonnull) detail_do(void *instance, REQUEST *request, RADIUS_PACKET *packet, bool compat)
{
	int		outfd;
	char		buffer[DIRLEN];

	FILE		*outfp;

#ifdef HAVE_GRP_H
	gid_t		gid;
	char		*endptr;
#endif

	detail_instance_t *inst = instance;

	/*
	 *	Generate the path for the detail file.  Use the same
	 *	format, but truncate at the last /.  Then feed it
	 *	through radius_xlat() to expand the variables.
	 */
	if (radius_xlat(buffer, sizeof(buffer), request, inst->filename, inst->escape_func, NULL) < 0) {
		return RLM_MODULE_FAIL;
	}

	RDEBUG2("%s expands to %s", inst->filename, buffer);

#ifdef WITH_ACCOUNTING
#if defined(HAVE_FNMATCH_H) && defined(FNM_FILE_NAME)
	/*
	 *	If we read it from a detail file, and we're about to
	 *	write it back to the SAME detail file directory, then
	 *	suppress the write.  This check prevents an infinite
	 *	loop.
	 */
	if ((request->listener->type == RAD_LISTEN_DETAIL) &&
	    (fnmatch(((listen_detail_t *)request->listener->data)->filename,
		     buffer, FNM_FILE_NAME | FNM_PERIOD ) == 0)) {
		RWDEBUG2("Suppressing infinite loop");
		return RLM_MODULE_NOOP;
	}
#endif
#endif

	outfd = exfile_open(inst->ef, buffer, inst->perm, true);
	if (outfd < 0) {
		RERROR("Couldn't open file %s: %s", buffer, fr_strerror());
		return RLM_MODULE_FAIL;
	}

	if (inst->group != NULL) {
		gid = strtol(inst->group, &endptr, 10);
		if (*endptr != '\0') {
			if (rad_getgid(request, &gid, inst->group) < 0) {
				RDEBUG2("Unable to find system group '%s'", inst->group);
				goto skip_group;
			}
		}

		if (chown(buffer, -1, gid) == -1) {
			RDEBUG2("Unable to change system group of '%s'", buffer);
		}
	}

skip_group:
	/*
	 *	Open the output fp for buffering.
	 */
	if ((outfp = fdopen(outfd, "a")) == NULL) {
		RERROR("Couldn't open file %s: %s", buffer, fr_syserror(errno));
	fail:
		if (outfp) fclose(outfp);
		exfile_unlock(inst->ef, outfd);
		return RLM_MODULE_FAIL;
	}

	if (detail_write(outfp, inst, request, packet, compat) < 0) goto fail;

	/*
	 *	Flush everything
	 */
	fclose(outfp);
	exfile_unlock(inst->ef, outfd); /* do NOT close outfp */

	/*
	 *	And everything is fine.
	 */
	return RLM_MODULE_OK;
}