/*
 * Copyright (c) Crackerjack Project., 2007-2008 ,Hitachi, Ltd
 *          Author(s): Takahiro Yasui <takahiro.yasui.mp@hitachi.com>,
 *		       Yumiko Sugita <yumiko.sugita.yf@hitachi.com>,
 *		       Satoshi Fujiwara <sa-fuji@sdl.hitachi.co.jp>
 * Copyright (c) 2016 Linux Test Project
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it would be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write the Free Software Foundation,
 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <errno.h>
#include <pwd.h>
#include <mqueue.h>

#include "tst_test.h"
#include "tst_safe_posix_ipc.h"

#define QUEUE_NAME	"/test_mqueue"

static uid_t euid;
static struct passwd *pw;

struct test_case {
	int as_nobody;
	char *qname;
	int ret;
	int err;
};

static struct test_case tcase[] = {
	{
		.qname = QUEUE_NAME,
		.ret = 0,
		.err = 0,
	},
	{
		.as_nobody = 1,
		.qname = QUEUE_NAME,
		.ret = -1,
		.err = EACCES,
	},
	{
		.qname = "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
			"aaaaaaaaaaaaaaa",
		.ret = -1,
		.err = ENOENT,
	},
	{
		.qname = "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
			"aaaaaaaaaaaaaaaa",
		.ret = -1,
		.err = ENAMETOOLONG,
	},
};

void setup(void)
{
	euid = geteuid();
	pw = SAFE_GETPWNAM("nobody");
}

static void do_test(unsigned int i)
{
	struct test_case *tc = &tcase[i];
	mqd_t fd;

	tst_res(TINFO, "queue name %s", tc->qname);

	/*
	 * When test ended with SIGTERM etc, mq descriptor is left remains.
	 * So we delete it first.
	 */
	mq_unlink(QUEUE_NAME);

	/* prepare */
	fd = SAFE_MQ_OPEN(QUEUE_NAME, O_CREAT | O_EXCL | O_RDWR, S_IRWXU, NULL);

	if (tc->as_nobody && seteuid(pw->pw_uid)) {
		tst_res(TBROK | TERRNO, "seteuid failed");
		goto EXIT;
	}

	/* test */
	TEST(mq_unlink(tc->qname));
	if (TEST_ERRNO != tc->err || TEST_RETURN != tc->ret) {
		tst_res(TFAIL | TTERRNO, "mq_unlink returned %ld, expected %d,"
			" expected errno %s (%d)", TEST_RETURN,
			tc->ret, tst_strerrno(tc->err), tc->err);
	} else {
		tst_res(TPASS | TTERRNO, "mq_unlink returned %ld", TEST_RETURN);
	}

EXIT:
	/* cleanup */
	if (tc->as_nobody && seteuid(euid) == -1)
		tst_res(TWARN | TERRNO, "seteuid back to %d failed", euid);

	if (fd > 0 && close(fd))
		tst_res(TWARN | TERRNO, "close(fd) failed");

	mq_unlink(QUEUE_NAME);
}

static struct tst_test test = {
	.tcnt = ARRAY_SIZE(tcase),
	.test = do_test,
	.needs_root = 1,
	.setup = setup,
};
