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调用成功。
总结:
- gethostname保证成功时name末尾以NULL结尾;
- 如果name不足以存放真实的主机名以及结尾NULL字符,可能返回ENAMETOOLONG错误;
- 同样的代码,可能由于include不同的头文件,具有不同的代码逻辑,差异可能很大!
- 在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,考虑边界值!