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,考慮邊界值!