Linux:调用gethostname返回ENAMETOOLONG(File name too long)

Linux:调用gethostname返回ENAMETOOLONG(File name too long)

今天遇到特别奇怪的一个问题。

代码:

main.c

#include "util.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 256
#endif

// Attention
#include <limits.h>

void main()
{
	display();

	printf("%s %d %s: HOST_NAME_MAX=%d\n", __FILE__, __LINE__, __func__, HOST_NAME_MAX);

	char buff[HOST_NAME_MAX] = "localhost";
	if (gethostname(buff, sizeof(buff)) != 0)
	{
		printf("%s %d %s: gethostname error: %s\n", __FILE__, __LINE__, __func__, strerror(errno));
		exit(1);
	}
	printf("%s %d %s: gethostname ok: %s\n", __FILE__, __LINE__, __func__, buff);
	exit(0);
}

util.h

#ifndef _UTIL_H
#define _UTIL_H

void display();

#endif

util.c

#include "util.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 256
#endif

void display()
{
	printf("%s %d %s: HOST_NAME_MAX=%d\n", __FILE__, __LINE__, __func__, HOST_NAME_MAX);

	char buff[HOST_NAME_MAX] = "localhost";
	if (gethostname(buff, sizeof(buff)) != 0)
	{
		printf("%s %d %s: gethostname error: %s\n", __FILE__, __LINE__, __func__, strerror(errno));
		exit(1);
	}
	printf("%s %d %s: gethostname ok: %s\n", __FILE__, __LINE__, __func__, buff);
}

编译 & 运行:

[test1280@localhost 20190820]$ gcc -o main main.c util.c -g
[test1280@localhost 20190820]$ ./main
util.c 14 display: HOST_NAME_MAX=256
util.c 22 display: gethostname ok: 5a327fb801fd4e6dbcfd7dd22b1e5df2-vm-24-xx-xxx-portal-1.novalocal
main.c 19 main: HOST_NAME_MAX=64
main.c 24 main: gethostname error: File name too long

嗯?一样的调用gethostname,在不同的文件中执行,为什么行为不一致呢?

首先注意到,HOST_NAME_MAX值不同。

在main.c中,include的limits.h(递归包含)定义宏HOST_NAME_MAX是64,而在util.c包含的头文件中没有定义此宏,因此使用自定义的宏256。

其次,查看当前主机名:

[test1280@localhost 20190820]$ hostname
5a327fb801fd4e6dbcfd7dd22b1e5df2-vm-24-xx-xxx-portal-1.novalocal

当前主机名恰好是64字节长度。

最后,通过man查看gethostname手册:

int gethostname(char *name, size_t len);

gethostname() returns the null-terminated hostname in the character array name, 
which has a length of len bytes.
If the null-terminated  hostname is  too  large  to  fit,  then  the  name is truncated, and no error is returned (but see NOTES below).
POSIX.1-2001 says that if such truncation occurs, then it is unspecified whether the returned buffer includes a terminating null byte.

SUSv2 guarantees that "Host names are limited to 255 bytes".

POSIX.1-2001 guarantees that "Host names (not including the terminating  null  byte) are limited to HOST_NAME_MAX bytes".

On Linux, HOST_NAME_MAX is defined with the value 64, which has been the limit since Linux 1.0 (earlier kernels imposed a limit of 8 bytes).

gethostname保证name最后以NULL结尾。

也就是说,如果主机名是64字节,通过gethostname获取主机名,至少要保证name大小是64+1字节。

多余的1字节用于存储NULL字符(0x00、’\0’)。

如果name空间不足以存放hostname真实值以及NULL字符,可能主机名被截断,也可能返回ENAMETOOLONG

再看我们的样例:

在main中系统定义HOST_NAME_MAX是64,主机名恰好是64字节,通过gethostname调用,第二个参数指明name最大64字节,此时gethostname返回失败,因为至少需要65字节才能正确存放。(没有自动截断)

在util中,使用自定义宏是256,name大小也是256字节,足够存放65字节的有效信息,因此gethostname调用成功。


总结:

  1. gethostname保证成功时name末尾以NULL结尾;
  2. 如果name不足以存放真实的主机名以及结尾NULL字符,可能返回ENAMETOOLONG错误;
  3. 同样的代码,可能由于include不同的头文件,具有不同的代码逻辑,差异可能很大!
  4. 在Linux中,HOST_NAME_MAX通常是64字节;

设置64字节主机名,成功:

[root@localhost ~]# hostname 5a327fb801fd4e6dbcfd7dd22b1e5df2-vm-24-xx-xxx-portal-1.novalocal
[root@localhost ~]# hostname
5a327fb801fd4e6dbcfd7dd22b1e5df2-vm-24-xx-xxx-portal-1.novalocal

设置65字节主机名,失败:

[root@localhost ~]# hostname 5a327fb801fd4e6dbcfd7dd22b1e5df2-vm-24-xx-xxx-portal-1.novalocalx
hostname: name too long

优化:

#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 255
#endif

char buff[HOST_NAME_MAX+1] = "localhost";

分配缓冲区时,主机名最大长度+1,考虑边界值!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值