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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章