1/*
2 * ata_id - reads product/serial number from ATA drives
3 *
4 * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
5 *
6 *	This program is free software; you can redistribute it and/or modify it
7 *	under the terms of the GNU General Public License as published by the
8 *	Free Software Foundation version 2 of the License.
9 */
10
11#ifndef _GNU_SOURCE
12#define _GNU_SOURCE 1
13#endif
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <unistd.h>
18#include <fcntl.h>
19#include <ctype.h>
20#include <string.h>
21#include <errno.h>
22#include <getopt.h>
23#include <sys/ioctl.h>
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <linux/types.h>
27#include <linux/hdreg.h>
28
29#include "../../udev.h"
30
31#ifdef USE_LOG
32void log_message(int priority, const char *format, ...)
33{
34	va_list args;
35	static int udev_log = -1;
36
37	if (udev_log == -1) {
38		const char *value;
39
40		value = getenv("UDEV_LOG");
41		if (value)
42			udev_log = log_priority(value);
43		else
44			udev_log = LOG_ERR;
45	}
46
47	if (priority > udev_log)
48		return;
49
50	va_start(args, format);
51	vsyslog(priority, format, args);
52	va_end(args);
53}
54#endif
55
56static void set_str(char *to, const char *from, size_t count)
57{
58	size_t i, j, len;
59
60	/* strip trailing whitespace */
61	len = strnlen(from, count);
62	while (len && isspace(from[len-1]))
63		len--;
64
65	/* strip leading whitespace */
66	i = 0;
67	while (isspace(from[i]) && (i < len))
68		i++;
69
70	j = 0;
71	while (i < len) {
72		/* substitute multiple whitespace */
73		if (isspace(from[i])) {
74			while (isspace(from[i]))
75				i++;
76			to[j++] = '_';
77		}
78		/* skip chars */
79		if (from[i] == '/') {
80			i++;
81			continue;
82		}
83		to[j++] = from[i++];
84	}
85	to[j] = '\0';
86}
87
88int main(int argc, char *argv[])
89{
90	struct hd_driveid id;
91	char model[41];
92	char serial[21];
93	char revision[9];
94	const char *node = NULL;
95	int export = 0;
96	int fd;
97	int rc = 0;
98	static const struct option options[] = {
99		{ "export", 0, NULL, 'x' },
100		{ "help", 0, NULL, 'h' },
101		{}
102	};
103
104	logging_init("ata_id");
105
106	while (1) {
107		int option;
108
109		option = getopt_long(argc, argv, "xh", options, NULL);
110		if (option == -1)
111			break;
112
113		switch (option) {
114		case 'x':
115			export = 1;
116			break;
117		case 'h':
118			printf("Usage: ata_id [--export] [--help] <device>\n"
119			       "  --export    print values as environemt keys\n"
120			       "  --help      print this help text\n\n");
121		default:
122			rc = 1;
123			goto exit;
124		}
125	}
126
127	node = argv[optind];
128	if (node == NULL) {
129		err("no node specified");
130		rc = 1;
131		goto exit;
132	}
133
134	fd = open(node, O_RDONLY|O_NONBLOCK);
135	if (fd < 0) {
136		err("unable to open '%s'", node);
137		rc = 1;
138		goto exit;
139	}
140
141	if (ioctl(fd, HDIO_GET_IDENTITY, &id)) {
142		if (errno == ENOTTY) {
143			info("HDIO_GET_IDENTITY unsupported for '%s'", node);
144			rc = 2;
145		} else {
146			err("HDIO_GET_IDENTITY failed for '%s'", node);
147			rc = 3;
148		}
149		goto close;
150	}
151
152	set_str(model, (char *) id.model, 40);
153	set_str(serial, (char *) id.serial_no, 20);
154	set_str(revision, (char *) id.fw_rev, 8);
155
156	if (export) {
157		if ((id.config >> 8) & 0x80) {
158			/* This is an ATAPI device */
159			switch ((id.config >> 8) & 0x1f) {
160			case 0:
161				printf("ID_TYPE=cd\n");
162				break;
163			case 1:
164				printf("ID_TYPE=tape\n");
165				break;
166			case 5:
167				printf("ID_TYPE=cd\n");
168				break;
169			case 7:
170				printf("ID_TYPE=optical\n");
171				break;
172			default:
173				printf("ID_TYPE=generic\n");
174				break;
175			}
176		} else {
177			printf("ID_TYPE=disk\n");
178		}
179		printf("ID_MODEL=%s\n", model);
180		printf("ID_SERIAL=%s\n", serial);
181		printf("ID_REVISION=%s\n", revision);
182		printf("ID_BUS=ata\n");
183	} else {
184		if (serial[0] != '\0')
185			printf("%s_%s\n", model, serial);
186		else
187			printf("%s\n", model);
188	}
189
190close:
191	close(fd);
192exit:
193	logging_close();
194	return rc;
195}
196