Blame axfer/container.c

Packit Service a9274b
// SPDX-License-Identifier: GPL-2.0
Packit Service a9274b
//
Packit Service a9274b
// container.c - an interface of parser/builder for formatted files.
Packit Service a9274b
//
Packit Service a9274b
// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
Packit Service a9274b
//
Packit Service a9274b
// Licensed under the terms of the GNU General Public License, version 2.
Packit Service a9274b
Packit Service a9274b
#include "container.h"
Packit Service a9274b
#include "misc.h"
Packit Service a9274b
Packit Service a9274b
#include <stdio.h>
Packit Service a9274b
#include <errno.h>
Packit Service a9274b
#include <string.h>
Packit Service a9274b
#include <fcntl.h>
Packit Service a9274b
#include <inttypes.h>
Packit Service a9274b
Packit Service a9274b
static const char *const cntr_type_labels[] = {
Packit Service a9274b
	[CONTAINER_TYPE_PARSER] = "parser",
Packit Service a9274b
	[CONTAINER_TYPE_BUILDER] = "builder",
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
static const char *const cntr_format_labels[] = {
Packit Service a9274b
	[CONTAINER_FORMAT_RIFF_WAVE] = "riff/wave",
Packit Service a9274b
	[CONTAINER_FORMAT_AU] = "au",
Packit Service a9274b
	[CONTAINER_FORMAT_VOC] = "voc",
Packit Service a9274b
	[CONTAINER_FORMAT_RAW] = "raw",
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
static const char *const suffixes[] = {
Packit Service a9274b
	[CONTAINER_FORMAT_RIFF_WAVE]	= ".wav",
Packit Service a9274b
	[CONTAINER_FORMAT_AU]		= ".au",
Packit Service a9274b
	[CONTAINER_FORMAT_VOC]		= ".voc",
Packit Service a9274b
	[CONTAINER_FORMAT_RAW]		= "",
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
const char *const container_suffix_from_format(enum container_format format)
Packit Service a9274b
{
Packit Service a9274b
	return suffixes[format];
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
int container_recursive_read(struct container_context *cntr, void *buf,
Packit Service a9274b
			     unsigned int byte_count)
Packit Service a9274b
{
Packit Service a9274b
	char *dst = buf;
Packit Service a9274b
	ssize_t result;
Packit Service a9274b
	size_t consumed = 0;
Packit Service a9274b
Packit Service a9274b
	while (consumed < byte_count && !cntr->interrupted) {
Packit Service a9274b
		result = read(cntr->fd, dst + consumed, byte_count - consumed);
Packit Service a9274b
		if (result < 0) {
Packit Service a9274b
			// This descriptor was configured with non-blocking
Packit Service a9274b
			// mode. EINTR is not cought when get any interrupts.
Packit Service a9274b
			if (cntr->interrupted)
Packit Service a9274b
				return -EINTR;
Packit Service a9274b
			if (errno == EAGAIN)
Packit Service a9274b
				continue;
Packit Service a9274b
			return -errno;
Packit Service a9274b
		}
Packit Service a9274b
		// Reach EOF.
Packit Service a9274b
		if (result == 0) {
Packit Service a9274b
			cntr->eof = true;
Packit Service a9274b
			return 0;
Packit Service a9274b
		}
Packit Service a9274b
Packit Service a9274b
		consumed += result;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
int container_recursive_write(struct container_context *cntr, void *buf,
Packit Service a9274b
			      unsigned int byte_count)
Packit Service a9274b
{
Packit Service a9274b
	char *src = buf;
Packit Service a9274b
	ssize_t result;
Packit Service a9274b
	size_t consumed = 0;
Packit Service a9274b
Packit Service a9274b
	while (consumed < byte_count && !cntr->interrupted) {
Packit Service a9274b
		result = write(cntr->fd, src + consumed, byte_count - consumed);
Packit Service a9274b
		if (result < 0) {
Packit Service a9274b
			// This descriptor was configured with non-blocking
Packit Service a9274b
			// mode. EINTR is not cought when get any interrupts.
Packit Service a9274b
			if (cntr->interrupted)
Packit Service a9274b
				return -EINTR;
Packit Service a9274b
			if (errno == EAGAIN)
Packit Service a9274b
				continue;
Packit Service a9274b
			return -errno;
Packit Service a9274b
		}
Packit Service a9274b
Packit Service a9274b
		consumed += result;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
enum container_format container_format_from_path(const char *path)
Packit Service a9274b
{
Packit Service a9274b
	const char *suffix;
Packit Service a9274b
	const char *pos;
Packit Service a9274b
	int i;
Packit Service a9274b
Packit Service a9274b
	for (i = 0; i < ARRAY_SIZE(suffixes); ++i) {
Packit Service a9274b
		suffix = suffixes[i];
Packit Service a9274b
Packit Service a9274b
		// Check last part of the string.
Packit Service a9274b
		pos = path + strlen(path) - strlen(suffix);
Packit Service a9274b
		if (!strcmp(pos, suffix))
Packit Service a9274b
			return i;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// Unsupported.
Packit Service a9274b
	return CONTAINER_FORMAT_RAW;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
int container_seek_offset(struct container_context *cntr, off64_t offset)
Packit Service a9274b
{
Packit Service a9274b
	off64_t pos;
Packit Service a9274b
Packit Service a9274b
	pos = lseek64(cntr->fd, offset, SEEK_SET);
Packit Service a9274b
	if (pos < 0)
Packit Service a9274b
		return -errno;
Packit Service a9274b
	if (pos != offset)
Packit Service a9274b
		return -EIO;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
// To avoid blocking execution at system call iteration after receiving UNIX
Packit Service a9274b
// signals.
Packit Service a9274b
static int set_nonblock_flag(int fd)
Packit Service a9274b
{
Packit Service a9274b
	int flags;
Packit Service a9274b
Packit Service a9274b
	flags = fcntl(fd, F_GETFL);
Packit Service a9274b
	if (flags < 0)
Packit Service a9274b
		return -errno;
Packit Service a9274b
Packit Service a9274b
	flags |= O_NONBLOCK;
Packit Service a9274b
	if (fcntl(fd, F_SETFL, flags) < 0)
Packit Service a9274b
		return -errno;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
int container_parser_init(struct container_context *cntr,
Packit Service a9274b
			  const char *const path, unsigned int verbose)
Packit Service a9274b
{
Packit Service a9274b
	const struct container_parser *parsers[] = {
Packit Service a9274b
		[CONTAINER_FORMAT_RIFF_WAVE] = &container_parser_riff_wave,
Packit Service a9274b
		[CONTAINER_FORMAT_AU] = &container_parser_au,
Packit Service a9274b
		[CONTAINER_FORMAT_VOC] = &container_parser_voc,
Packit Service a9274b
	};
Packit Service a9274b
	const struct container_parser *parser;
Packit Service a9274b
	unsigned int size;
Packit Service a9274b
	int i;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	assert(cntr);
Packit Service a9274b
	assert(path);
Packit Service a9274b
	assert(path[0] != '\0');
Packit Service a9274b
Packit Service a9274b
	// Detect forgotten to destruct.
Packit Service a9274b
	assert(cntr->fd == 0);
Packit Service a9274b
	assert(cntr->private_data == NULL);
Packit Service a9274b
Packit Service a9274b
	memset(cntr, 0, sizeof(*cntr));
Packit Service a9274b
Packit Service a9274b
	// Open a target descriptor.
Packit Service a9274b
	if (!strcmp(path, "-")) {
Packit Service a9274b
		cntr->fd = fileno(stdin);
Packit Service a9274b
		if (isatty(cntr->fd)) {
Packit Service a9274b
			fprintf(stderr,
Packit Service a9274b
				"A terminal is referred for standard input. "
Packit Service a9274b
				"Output from any process or shell redirection "
Packit Service a9274b
				"should be referred instead.\n");
Packit Service a9274b
			return -EIO;
Packit Service a9274b
		}
Packit Service a9274b
		err = set_nonblock_flag(cntr->fd);
Packit Service a9274b
		if (err < 0)
Packit Service a9274b
			return err;
Packit Service a9274b
		cntr->stdio = true;
Packit Service a9274b
	} else {
Packit Service a9274b
		cntr->fd = open(path, O_RDONLY | O_NONBLOCK);
Packit Service a9274b
		if (cntr->fd < 0)
Packit Service a9274b
			return -errno;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// 4 bytes are enough to detect supported containers.
Packit Service a9274b
	err = container_recursive_read(cntr, cntr->magic, sizeof(cntr->magic));
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
	for (i = 0; i < ARRAY_SIZE(parsers); ++i) {
Packit Service a9274b
		parser = parsers[i];
Packit Service a9274b
		size = strlen(parser->magic);
Packit Service a9274b
		if (size > 4)
Packit Service a9274b
			size = 4;
Packit Service a9274b
		if (!strncmp(cntr->magic, parser->magic, size))
Packit Service a9274b
			break;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// Don't forget that the first 4 bytes were already read for magic
Packit Service a9274b
	// bytes.
Packit Service a9274b
	cntr->magic_handled = false;
Packit Service a9274b
Packit Service a9274b
	// Unless detected, use raw container.
Packit Service a9274b
	if (i == ARRAY_SIZE(parsers))
Packit Service a9274b
		parser = &container_parser_raw;
Packit Service a9274b
Packit Service a9274b
	// Allocate private data for the parser.
Packit Service a9274b
	if (parser->private_size > 0) {
Packit Service a9274b
		cntr->private_data = malloc(parser->private_size);
Packit Service a9274b
		if (cntr->private_data == NULL)
Packit Service a9274b
			return -ENOMEM;
Packit Service a9274b
		memset(cntr->private_data, 0, parser->private_size);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	cntr->type = CONTAINER_TYPE_PARSER;
Packit Service a9274b
	cntr->process_bytes = container_recursive_read;
Packit Service a9274b
	cntr->format = parser->format;
Packit Service a9274b
	cntr->ops = &parser->ops;
Packit Service a9274b
	cntr->max_size = parser->max_size;
Packit Service a9274b
	cntr->verbose = verbose;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
int container_builder_init(struct container_context *cntr,
Packit Service a9274b
			   const char *const path, enum container_format format,
Packit Service a9274b
			   unsigned int verbose)
Packit Service a9274b
{
Packit Service a9274b
	const struct container_builder *builders[] = {
Packit Service a9274b
		[CONTAINER_FORMAT_RIFF_WAVE] = &container_builder_riff_wave,
Packit Service a9274b
		[CONTAINER_FORMAT_AU] = &container_builder_au,
Packit Service a9274b
		[CONTAINER_FORMAT_VOC] = &container_builder_voc,
Packit Service a9274b
		[CONTAINER_FORMAT_RAW] = &container_builder_raw,
Packit Service a9274b
	};
Packit Service a9274b
	const struct container_builder *builder;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	assert(cntr);
Packit Service a9274b
	assert(path);
Packit Service a9274b
	assert(path[0] != '\0');
Packit Service a9274b
Packit Service a9274b
	// Detect forgotten to destruct.
Packit Service a9274b
	assert(cntr->fd == 0);
Packit Service a9274b
	assert(cntr->private_data == NULL);
Packit Service a9274b
Packit Service a9274b
	memset(cntr, 0, sizeof(*cntr));
Packit Service a9274b
Packit Service a9274b
	// Open a target descriptor.
Packit Service a9274b
	if (path == NULL || *path == '\0')
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
	if (!strcmp(path, "-")) {
Packit Service a9274b
		cntr->fd = fileno(stdout);
Packit Service a9274b
		if (isatty(cntr->fd)) {
Packit Service a9274b
			fprintf(stderr,
Packit Service a9274b
				"A terminal is referred for standard output. "
Packit Service a9274b
				"Input to any process or shell redirection "
Packit Service a9274b
				"should be referred instead.\n");
Packit Service a9274b
			return -EIO;
Packit Service a9274b
		}
Packit Service a9274b
		err = set_nonblock_flag(cntr->fd);
Packit Service a9274b
		if (err < 0)
Packit Service a9274b
			return err;
Packit Service a9274b
		cntr->stdio = true;
Packit Service a9274b
	} else {
Packit Service a9274b
		cntr->fd = open(path, O_RDWR | O_NONBLOCK | O_CREAT | O_TRUNC,
Packit Service a9274b
				0644);
Packit Service a9274b
		if (cntr->fd < 0)
Packit Service a9274b
			return -errno;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	builder = builders[format];
Packit Service a9274b
Packit Service a9274b
	// Allocate private data for the builder.
Packit Service a9274b
	if (builder->private_size > 0) {
Packit Service a9274b
		cntr->private_data = malloc(builder->private_size);
Packit Service a9274b
		if (cntr->private_data == NULL)
Packit Service a9274b
			return -ENOMEM;
Packit Service a9274b
		memset(cntr->private_data, 0, builder->private_size);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	cntr->type = CONTAINER_TYPE_BUILDER;
Packit Service a9274b
	cntr->process_bytes = container_recursive_write;
Packit Service a9274b
	cntr->format = builder->format;
Packit Service a9274b
	cntr->ops = &builder->ops;
Packit Service a9274b
	cntr->max_size = builder->max_size;
Packit Service a9274b
	cntr->verbose = verbose;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
int container_context_pre_process(struct container_context *cntr,
Packit Service a9274b
				  snd_pcm_format_t *format,
Packit Service a9274b
				  unsigned int *samples_per_frame,
Packit Service a9274b
				  unsigned int *frames_per_second,
Packit Service a9274b
				  uint64_t *frame_count)
Packit Service a9274b
{
Packit Service a9274b
	uint64_t byte_count = 0;
Packit Service a9274b
	unsigned int bytes_per_frame;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	assert(cntr);
Packit Service a9274b
	assert(format);
Packit Service a9274b
	assert(samples_per_frame);
Packit Service a9274b
	assert(frames_per_second);
Packit Service a9274b
	assert(frame_count);
Packit Service a9274b
Packit Service a9274b
	if (cntr->type == CONTAINER_TYPE_BUILDER)
Packit Service a9274b
		byte_count = cntr->max_size;
Packit Service a9274b
Packit Service a9274b
	if (cntr->ops->pre_process) {
Packit Service a9274b
		err = cntr->ops->pre_process(cntr, format, samples_per_frame,
Packit Service a9274b
					     frames_per_second, &byte_count);
Packit Service a9274b
		if (err < 0)
Packit Service a9274b
			return err;
Packit Service a9274b
		if (cntr->eof)
Packit Service a9274b
			return 0;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	if (cntr->format == CONTAINER_FORMAT_RAW) {
Packit Service a9274b
		if (*format == SND_PCM_FORMAT_UNKNOWN ||
Packit Service a9274b
		    *samples_per_frame == 0 || *frames_per_second == 0) {
Packit Service a9274b
			fprintf(stderr,
Packit Service a9274b
				"Any file format is not detected. Need to "
Packit Service a9274b
				"indicate all of sample format, channels and "
Packit Service a9274b
				"rate explicitly.\n");
Packit Service a9274b
			return -EINVAL;
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
	assert(*format >= SND_PCM_FORMAT_S8);
Packit Service a9274b
	assert(*format <= SND_PCM_FORMAT_LAST);
Packit Service a9274b
	assert(*samples_per_frame > 0);
Packit Service a9274b
	assert(*frames_per_second > 0);
Packit Service a9274b
	assert(byte_count > 0);
Packit Service a9274b
Packit Service a9274b
	cntr->bytes_per_sample = snd_pcm_format_physical_width(*format) / 8;
Packit Service a9274b
	cntr->samples_per_frame = *samples_per_frame;
Packit Service a9274b
	cntr->frames_per_second = *frames_per_second;
Packit Service a9274b
Packit Service a9274b
	bytes_per_frame = cntr->bytes_per_sample * *samples_per_frame;
Packit Service a9274b
	*frame_count = byte_count / bytes_per_frame;
Packit Service a9274b
	cntr->max_size -= cntr->max_size / bytes_per_frame;
Packit Service a9274b
Packit Service a9274b
	if (cntr->verbose > 0) {
Packit Service a9274b
		fprintf(stderr, "Container: %s\n",
Packit Service a9274b
			cntr_type_labels[cntr->type]);
Packit Service a9274b
		fprintf(stderr, "  format: %s\n",
Packit Service a9274b
			cntr_format_labels[cntr->format]);
Packit Service a9274b
		fprintf(stderr, "  sample format: %s\n",
Packit Service a9274b
			snd_pcm_format_name(*format));
Packit Service a9274b
		fprintf(stderr, "  bytes/sample: %u\n",
Packit Service a9274b
			cntr->bytes_per_sample);
Packit Service a9274b
		fprintf(stderr, "  samples/frame: %u\n",
Packit Service a9274b
			cntr->samples_per_frame);
Packit Service a9274b
		fprintf(stderr, "  frames/second: %u\n",
Packit Service a9274b
			cntr->frames_per_second);
Packit Service a9274b
		if (cntr->type == CONTAINER_TYPE_PARSER) {
Packit Service a9274b
			fprintf(stderr, "  frames: %" PRIu64 "\n",
Packit Service a9274b
				*frame_count);
Packit Service a9274b
		} else {
Packit Service a9274b
			fprintf(stderr, "  max frames: %" PRIu64 "\n",
Packit Service a9274b
				*frame_count);
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
int container_context_process_frames(struct container_context *cntr,
Packit Service a9274b
				     void *frame_buffer,
Packit Service a9274b
				     unsigned int *frame_count)
Packit Service a9274b
{
Packit Service a9274b
	char *buf = frame_buffer;
Packit Service a9274b
	unsigned int bytes_per_frame;
Packit Service a9274b
	unsigned int byte_count;
Packit Service a9274b
	unsigned int target_byte_count;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	assert(cntr);
Packit Service a9274b
	assert(!cntr->eof);
Packit Service a9274b
	assert(frame_buffer);
Packit Service a9274b
	assert(frame_count);
Packit Service a9274b
Packit Service a9274b
	bytes_per_frame = cntr->bytes_per_sample * cntr->samples_per_frame;
Packit Service a9274b
	target_byte_count = *frame_count * bytes_per_frame;
Packit Service a9274b
Packit Service a9274b
	// A parser of cotainers already read first 4 bytes to detect format
Packit Service a9274b
	// of container, however they includes PCM frames when any format was
Packit Service a9274b
	// undetected. Surely to write out them.
Packit Service a9274b
	byte_count = target_byte_count;
Packit Service a9274b
	if (cntr->format == CONTAINER_FORMAT_RAW &&
Packit Service a9274b
	    cntr->type == CONTAINER_TYPE_PARSER && !cntr->magic_handled) {
Packit Service a9274b
		memcpy(buf, cntr->magic, sizeof(cntr->magic));
Packit Service a9274b
		buf += sizeof(cntr->magic);
Packit Service a9274b
		byte_count -= sizeof(cntr->magic);
Packit Service a9274b
		cntr->magic_handled = true;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// Each container has limitation for its volume for sample data.
Packit Service a9274b
	if (cntr->handled_byte_count > cntr->max_size - byte_count)
Packit Service a9274b
		byte_count = cntr->max_size - cntr->handled_byte_count;
Packit Service a9274b
Packit Service a9274b
	// All of supported containers include interleaved PCM frames.
Packit Service a9274b
	// TODO: process frames for truncate case.
Packit Service a9274b
	err = cntr->process_bytes(cntr, buf, byte_count);
Packit Service a9274b
	if (err < 0) {
Packit Service a9274b
		*frame_count = 0;
Packit Service a9274b
		return err;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	cntr->handled_byte_count += target_byte_count;
Packit Service a9274b
	if (cntr->handled_byte_count == cntr->max_size)
Packit Service a9274b
		cntr->eof = true;
Packit Service a9274b
Packit Service a9274b
	*frame_count = target_byte_count / bytes_per_frame;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
int container_context_post_process(struct container_context *cntr,
Packit Service a9274b
				   uint64_t *frame_count)
Packit Service a9274b
{
Packit Service a9274b
	int err = 0;
Packit Service a9274b
Packit Service a9274b
	assert(cntr);
Packit Service a9274b
	assert(frame_count);
Packit Service a9274b
Packit Service a9274b
	if (cntr->verbose && cntr->handled_byte_count > 0) {
Packit Service a9274b
		fprintf(stderr, "  Handled bytes: %" PRIu64 "\n",
Packit Service a9274b
			cntr->handled_byte_count);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// NOTE* we cannot seek when using standard input/output.
Packit Service a9274b
	if (!cntr->stdio && cntr->ops && cntr->ops->post_process) {
Packit Service a9274b
		// Usually, need to write out processed bytes in container
Packit Service a9274b
		// header even it this program is interrupted.
Packit Service a9274b
		cntr->interrupted = false;
Packit Service a9274b
Packit Service a9274b
		err = cntr->ops->post_process(cntr, cntr->handled_byte_count);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// Ensure to perform write-back from disk cache.
Packit Service a9274b
	if (cntr->type == CONTAINER_TYPE_BUILDER)
Packit Service a9274b
		fsync(cntr->fd);
Packit Service a9274b
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
Packit Service a9274b
	if (cntr->bytes_per_sample == 0 || cntr->samples_per_frame == 0) {
Packit Service a9274b
		*frame_count = 0;
Packit Service a9274b
	} else {
Packit Service a9274b
		*frame_count = cntr->handled_byte_count /
Packit Service a9274b
			       cntr->bytes_per_sample /
Packit Service a9274b
			       cntr->samples_per_frame;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
void container_context_destroy(struct container_context *cntr)
Packit Service a9274b
{
Packit Service a9274b
	assert(cntr);
Packit Service a9274b
Packit Service a9274b
	close(cntr->fd);
Packit Service a9274b
	if (cntr->private_data)
Packit Service a9274b
		free(cntr->private_data);
Packit Service a9274b
Packit Service a9274b
	cntr->fd = 0;
Packit Service a9274b
	cntr->private_data = NULL;
Packit Service a9274b
}