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