nrelease - fix/improve livecd
[dragonfly.git] / usr.sbin / burncd / burncd.c
1 /*-
2  * Copyright (c) 2000,2001,2002 Søren Schmidt <sos@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification, immediately at the beginning of the file.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * $FreeBSD: src/usr.sbin/burncd/burncd.c,v 1.10.2.6 2002/11/20 00:26:18 njl Exp $
29  */
30
31 #include <unistd.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <err.h>
36 #include <sysexits.h>
37 #include <fcntl.h>
38 #include <sys/errno.h>
39 #include <sys/ioctl.h>
40 #include <sys/stat.h>
41 #include <sys/cdio.h>
42 #include <sys/cdrio.h>
43 #include <sys/param.h>
44
45 #define BLOCKS  16
46
47 struct track_info {
48         int     file;
49         char    *file_name;
50         u_int   file_size;
51         int     block_size;
52         int     block_type;
53         int     pregap;
54         int     addr;
55 };
56 static struct track_info tracks[100];
57 static int fd, quiet, verbose, saved_block_size, notracks;
58
59 static void add_track(char *, int, int, int);
60 static void do_DAO(int, int);
61 static void do_TAO(int, int);
62 static int write_file(struct track_info *);
63 static int roundup_blocks(struct track_info *);
64 static void cue_ent(struct cdr_cue_entry *, int, int, int, int, int, int, int);
65 static void cleanup(int);
66 static void usage(void) __dead2;
67
68 int
69 main(int argc, char **argv)
70 {
71         int ch, arg, addr;
72         int dao = 0, eject = 0, fixate = 0, list = 0, multi = 0, preemp = 0;
73         int nogap = 0, speed = 4 * 177, test_write = 0;
74         int block_size = 0, block_type = 0, cdopen = 0;
75         const char *dev = "/dev/acd0c";
76
77         while ((ch = getopt(argc, argv, "def:lmnpqs:tv")) != -1) {
78                 switch (ch) {
79                 case 'd':
80                         dao = 1;
81                         break;
82
83                 case 'e':
84                         eject = 1;
85                         break;
86
87                 case 'f':
88                         dev = optarg;
89                         break;
90
91                 case 'l':
92                         list = 1;
93                         break;
94
95                 case 'm':
96                         multi = 1;
97                         break;
98
99                 case 'n':
100                         nogap = 1;
101                         break;
102
103                 case 'p':
104                         preemp = 1;
105                         break;
106
107                 case 'q':
108                         quiet = 1;
109                         break;
110
111                 case 's':
112                         if (strcasecmp("max", optarg) == 0)
113                                 speed = CDR_MAX_SPEED;
114                         else
115                                 speed = atoi(optarg) * 177;
116                         if (speed <= 0)
117                                 errx(EX_USAGE, "Invalid speed: %s", optarg);
118                         break;
119
120                 case 't':
121                         test_write = 1;
122                         break;
123
124                 case 'v':
125                         verbose = 1;
126                         break;
127
128                 default: 
129                         usage();
130                 }
131         }
132         argc -= optind;
133         argv += optind;
134
135         if (argc == 0)
136                 usage();
137
138         if ((fd = open(dev, O_RDWR, 0)) < 0)
139                 err(EX_NOINPUT, "open(%s)", dev);
140
141         if (ioctl(fd, CDRIOCGETBLOCKSIZE, &saved_block_size) < 0) 
142                 err(EX_IOERR, "ioctl(CDRIOCGETBLOCKSIZE)");
143
144         if (ioctl(fd, CDRIOCWRITESPEED, &speed) < 0) 
145                 err(EX_IOERR, "ioctl(CDRIOCWRITESPEED)");
146
147         err_set_exit(cleanup);
148
149         for (arg = 0; arg < argc; arg++) {
150                 if (!strcasecmp(argv[arg], "fixate")) {
151                         fixate = 1;
152                         break;
153                 }
154                 if (!strcasecmp(argv[arg], "msinfo")) {
155                         struct ioc_read_toc_single_entry entry;
156                         struct ioc_toc_header header;
157
158                         if (ioctl(fd, CDIOREADTOCHEADER, &header) < 0) 
159                                 err(EX_IOERR, "ioctl(CDIOREADTOCHEADER)");
160                         bzero(&entry, sizeof(struct ioc_read_toc_single_entry));
161                         entry.address_format = CD_LBA_FORMAT;
162                         entry.track = header.ending_track;
163                         if (ioctl(fd, CDIOREADTOCENTRY, &entry) < 0) 
164                                 err(EX_IOERR, "ioctl(CDIOREADTOCENTRY)");
165                         if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0) 
166                                 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
167                         fprintf(stdout, "%d,%d\n", 
168                                 ntohl(entry.entry.addr.lba), addr);
169
170                         break;
171                 }
172                 if (!strcasecmp(argv[arg], "erase") || !strcasecmp(argv[arg], "blank")){
173                         int error, blank, percent;
174
175                         if (!strcasecmp(argv[arg], "erase"))
176                                 blank = CDR_B_ALL;
177                         else
178                                 blank = CDR_B_MIN;
179                         if (!quiet)
180                                 fprintf(stderr, "%sing CD, please wait..\r",
181                                         blank == CDR_B_ALL ? "eras" : "blank");
182
183                         if (ioctl(fd, CDRIOCBLANK, &blank) < 0)
184                                 err(EX_IOERR, "ioctl(CDRIOCBLANK)");
185                         while (1) {
186                                 sleep(1);
187                                 error = ioctl(fd, CDRIOCGETPROGRESS, &percent);
188                                 if (percent > 0 && !quiet)
189                                         fprintf(stderr, 
190                                                 "%sing CD - %d %% done     \r",
191                                                 blank == CDR_B_ALL ? 
192                                                 "eras" : "blank", percent);
193                                 if (error || percent == 100)
194                                         break;
195                         }
196                         if (!quiet)
197                                 printf("\n");
198                         continue;
199                 }
200                 if (!strcasecmp(argv[arg], "audio") || !strcasecmp(argv[arg], "raw")) {
201                         block_type = CDR_DB_RAW;
202                         block_size = 2352;
203                         continue;
204                 }
205                 if (!strcasecmp(argv[arg], "data") || !strcasecmp(argv[arg], "mode1")) {
206                         block_type = CDR_DB_ROM_MODE1;
207                         block_size = 2048;
208                         continue;
209                 }
210                 if (!strcasecmp(argv[arg], "mode2")) {
211                         block_type = CDR_DB_ROM_MODE2;
212                         block_size = 2336;
213                         continue;
214                 }
215                 if (!strcasecmp(argv[arg], "xamode1")) {
216                         block_type = CDR_DB_XA_MODE1;
217                         block_size = 2048;
218                         continue;
219                 }
220                 if (!strcasecmp(argv[arg], "xamode2")) {
221                         block_type = CDR_DB_XA_MODE2_F2;
222                         block_size = 2324;
223                         continue;
224                 }
225                 if (!strcasecmp(argv[arg], "vcd")) {
226                         block_type = CDR_DB_XA_MODE2_F2;
227                         block_size = 2352;
228                         dao = 1;
229                         nogap = 1;
230                         continue;
231                 }
232                 if (!block_size)
233                         errx(EX_NOINPUT, "no data format selected");
234                 if (list) {
235                         char file_buf[MAXPATHLEN + 1], *eol;
236                         FILE *fp;
237
238                         if ((fp = fopen(argv[arg], "r")) == NULL)
239                                 err(EX_NOINPUT, "fopen(%s)", argv[arg]);
240
241                         while (fgets(file_buf, sizeof(file_buf), fp) != NULL) {
242                                 if (*file_buf == '#' || *file_buf == '\n')
243                                         continue;
244                                 if ((eol = strchr(file_buf, '\n')))
245                                         *eol = 0;
246                                 add_track(file_buf, block_size, block_type, nogap);
247                         }
248                         if (feof(fp))
249                                 fclose(fp);
250                         else
251                                 err(EX_IOERR, "fgets(%s)", file_buf);
252                 }
253                 else
254                         add_track(argv[arg], block_size, block_type, nogap);
255         }
256         if (notracks) {
257                 if (ioctl(fd, CDIOCSTART, 0) < 0)
258                         err(EX_IOERR, "ioctl(CDIOCSTART)");
259                 if (!cdopen) {
260                         if (ioctl(fd, CDRIOCINITWRITER, &test_write) < 0)
261                                 err(EX_IOERR, "ioctl(CDRIOCINITWRITER)");
262                         cdopen = 1;
263                 }
264                 if (dao) 
265                         do_DAO(test_write, multi);
266                 else
267                         do_TAO(test_write, preemp);
268         }
269         if (fixate && !dao) {
270                 if (!quiet)
271                         fprintf(stderr, "fixating CD, please wait..\n");
272                 if (ioctl(fd, CDRIOCFIXATE, &multi) < 0)
273                         err(EX_IOERR, "ioctl(CDRIOCFIXATE)");
274         }
275
276         if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0) {
277                 err_set_exit(NULL);
278                 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
279         }
280
281         if (eject)
282                 if (ioctl(fd, CDIOCEJECT) < 0)
283                         err(EX_IOERR, "ioctl(CDIOCEJECT)");
284         close(fd);
285         exit(EX_OK);
286 }
287
288 static void
289 add_track(char *name, int block_size, int block_type, int nogap)
290 {
291         struct stat sb;
292         int file;
293         static int done_stdin = 0;
294
295         if (!strcmp(name, "-")) {
296                 if (done_stdin) {
297                         warn("skipping multiple usages of stdin");
298                         return;
299                 }
300                 file = STDIN_FILENO;
301                 done_stdin = 1;
302         }
303         else if ((file = open(name, O_RDONLY, 0)) < 0)
304                 err(EX_NOINPUT, "open(%s)", name);
305         if (fstat(file, &sb) < 0)
306                 err(EX_IOERR, "fstat(%s)", name);
307         tracks[notracks].file = file;
308         tracks[notracks].file_name = name;
309         if (file == STDIN_FILENO)
310                 tracks[notracks].file_size = -1;
311         else
312                 tracks[notracks].file_size = sb.st_size;
313         tracks[notracks].block_size = block_size;
314         tracks[notracks].block_type = block_type;
315
316         if (nogap && notracks)
317                 tracks[notracks].pregap = 0;
318         else {
319                 if (tracks[notracks - (notracks > 0)].block_type == block_type)
320                         tracks[notracks].pregap = 150;
321                 else
322                         tracks[notracks].pregap = 255;
323         }
324
325         if (verbose) {
326                 int pad = 0;
327
328                 if (tracks[notracks].file_size / tracks[notracks].block_size !=
329                     roundup_blocks(&tracks[notracks]))
330                         pad = 1;
331                 fprintf(stderr, 
332                         "adding type 0x%02x file %s size %d KB %d blocks %s\n",
333                         tracks[notracks].block_type, name, (int)sb.st_size/1024,
334                         roundup_blocks(&tracks[notracks]),
335                         pad ? "(0 padded)" : "");
336         }
337         notracks++;
338 }
339
340 static void
341 do_DAO(int test_write, int multi)
342 {
343         struct cdr_cuesheet sheet;
344         struct cdr_cue_entry cue[100];
345         int format = CDR_SESS_CDROM;
346         int addr, i, j = 0;
347
348         int bt2ctl[16] = { 0x0,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
349                            0x4, 0x4, 0x4, 0x4, 0x4, 0x4,  -1,  -1 };
350
351         int bt2df[16] = { 0x0,    -1,   -1,   -1,   -1,   -1,   -1,   -1,
352                           0x10, 0x30, 0x20,   -1, 0x21,   -1,   -1,   -1 };
353         
354         if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0) 
355                 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
356         if (verbose)
357                 fprintf(stderr, "next writeable LBA %d\n", addr);
358
359         cue_ent(&cue[j++], bt2ctl[tracks[0].block_type], 0x01, 0x00, 0x0,
360                 (bt2df[tracks[0].block_type] & 0xf0) | 
361                 (tracks[0].block_type < 8 ? 0x01 : 0x04), 0x00, addr);
362
363         for (i = 0; i < notracks; i++) {
364                 if (bt2ctl[tracks[i].block_type] < 0 ||
365                     bt2df[tracks[i].block_type] < 0)
366                         err(EX_IOERR, "track type not supported in DAO mode");
367
368                 if (tracks[i].block_type >= CDR_DB_XA_MODE1)
369                         format = CDR_SESS_CDROM_XA;
370
371                 if (i == 0) {
372                         addr += tracks[i].pregap;
373                         tracks[i].addr = addr;
374
375                         cue_ent(&cue[j++], bt2ctl[tracks[i].block_type], 
376                                 0x01, i+1, 0x1, bt2df[tracks[i].block_type],
377                                 0x00, addr);
378
379                 }
380                 else {
381                         if (tracks[i].pregap) {
382                                 if (tracks[i].block_type > 0x7) {
383                                         cue_ent(&cue[j++],bt2ctl[tracks[i].block_type], 
384                                                 0x01, i+1, 0x0,
385                                                 (bt2df[tracks[i].block_type] & 0xf0) | 
386                                                 (tracks[i].block_type < 8 ? 0x01 :0x04),
387                                                 0x00, addr);
388                                 }
389                                 else
390                                         cue_ent(&cue[j++],bt2ctl[tracks[i].block_type], 
391                                                 0x01, i+1, 0x0,
392                                                 bt2df[tracks[i].block_type],
393                                                 0x00, addr);
394                         }
395                         tracks[i].addr = tracks[i - 1].addr +
396                                 roundup_blocks(&tracks[i - 1]);
397
398                         cue_ent(&cue[j++], bt2ctl[tracks[i].block_type],
399                                 0x01, i+1, 0x1, bt2df[tracks[i].block_type],
400                                 0x00, addr + tracks[i].pregap);
401
402                         if (tracks[i].block_type > 0x7)
403                                 addr += tracks[i].pregap;
404                 }
405                 addr += roundup_blocks(&tracks[i]);
406         }
407
408         cue_ent(&cue[j++], bt2ctl[tracks[i - 1].block_type], 0x01, 0xaa, 0x01,
409                 (bt2df[tracks[i - 1].block_type] & 0xf0) | 
410                 (tracks[i - 1].block_type < 8 ? 0x01 : 0x04), 0x00, addr);
411
412         sheet.len = j * 8;
413         sheet.entries = cue;
414         sheet.test_write = test_write;
415         sheet.session_type = multi ? CDR_SESS_MULTI : CDR_SESS_NONE;
416         sheet.session_format = format;
417         if (verbose) {
418                 u_int8_t *ptr = (u_int8_t *)sheet.entries;
419                 
420                 fprintf(stderr,"CUE sheet:");
421                 for (i = 0; i < sheet.len; i++)
422                         if (i % 8)
423                                 fprintf(stderr," %02x", ptr[i]);
424                         else
425                                 fprintf(stderr,"\n%02x", ptr[i]);
426                 fprintf(stderr,"\n");
427         }
428         
429         if (ioctl(fd, CDRIOCSENDCUE, &sheet) < 0)
430                 err(EX_IOERR, "ioctl(CDRIOCSENDCUE)");
431
432         for (i = 0; i < notracks; i++) {
433                 if (write_file(&tracks[i]))
434                         err(EX_IOERR, "write_file");
435         }
436
437         ioctl(fd, CDRIOCFLUSH);
438 }
439
440 static void
441 do_TAO(int test_write, int preemp)
442 {
443         struct cdr_track track;
444         int i;
445
446         for (i = 0; i < notracks; i++) {
447                 track.test_write = test_write;
448                 track.datablock_type = tracks[i].block_type;
449                 track.preemp = preemp;
450                 if (ioctl(fd, CDRIOCINITTRACK, &track) < 0)
451                         err(EX_IOERR, "ioctl(CDRIOCINITTRACK)");
452
453                 if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &tracks[i].addr) < 0)
454                         err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
455                 if (!quiet)
456                         fprintf(stderr, "next writeable LBA %d\n",
457                                 tracks[i].addr);
458                 if (write_file(&tracks[i]))
459                         err(EX_IOERR, "write_file");
460                 if (ioctl(fd, CDRIOCFLUSH) < 0)
461                         err(EX_IOERR, "ioctl(CDRIOCFLUSH)");
462         }
463 }
464
465 static int
466 write_file(struct track_info *track_info)
467 {
468         int size, count, filesize;
469         char buf[2352*BLOCKS];
470         static int tot_size = 0;
471
472         filesize = track_info->file_size / 1024;
473
474         if (ioctl(fd, CDRIOCSETBLOCKSIZE, &track_info->block_size) < 0)
475                 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
476
477         if (track_info->addr >= 0)
478                 lseek(fd, track_info->addr * track_info->block_size, SEEK_SET);
479
480         if (verbose)
481                 fprintf(stderr, "addr = %d size = %d blocks = %d\n",
482                         track_info->addr, track_info->file_size,
483                         roundup_blocks(track_info));
484
485         if (!quiet) {
486                 if (track_info->file == STDIN_FILENO)
487                         fprintf(stderr, "writing from stdin\n");
488                 else
489                         fprintf(stderr, 
490                                 "writing from file %s size %d KB\n",
491                                 track_info->file_name, filesize);
492         }
493         size = 0;
494
495         while ((count = read(track_info->file, buf,
496                              MIN((track_info->file_size - size),
497                                  track_info->block_size * BLOCKS))) > 0) {      
498                 int res;
499
500                 if (count % track_info->block_size) {
501                         /* pad file to % block_size */
502                         bzero(&buf[count],
503                               (track_info->block_size * BLOCKS) - count);
504                         count = ((count / track_info->block_size) + 1) *
505                                 track_info->block_size;
506                 }
507                 if ((res = write(fd, buf, count)) != count) {
508                         fprintf(stderr, "\nonly wrote %d of %d bytes err=%d\n",
509                                 res, count, errno);
510                         break;
511                 }
512                 size += count;
513                 tot_size += count;
514                 if (!quiet) {
515                         int pct;
516
517                         fprintf(stderr, "written this track %d KB", size/1024);
518                         if (track_info->file != STDIN_FILENO && filesize) {
519                                 pct = (size / 1024) * 100 / filesize;
520                                 fprintf(stderr, " (%d%%)", pct);
521                         }
522                         fprintf(stderr, " total %d KB\r", tot_size/1024);
523                 }
524                 if (size >= track_info->file_size)
525                         break;
526         }
527
528         if (!quiet)
529                 fprintf(stderr, "\n");
530         close(track_info->file);
531         return 0;
532 }
533
534 static int
535 roundup_blocks(struct track_info *track)
536 {
537         return ((track->file_size + track->block_size - 1) / track->block_size);
538 }
539
540 static void
541 cue_ent(struct cdr_cue_entry *cue, int ctl, int adr, int track, int idx,
542         int dataform, int scms, int lba)
543 {
544         cue->adr = adr;
545         cue->ctl = ctl;
546         cue->track = track;
547         cue->index = idx;
548         cue->dataform = dataform;
549         cue->scms = scms;
550         lba += 150;
551         cue->min = lba / (60*75);
552         cue->sec = (lba % (60*75)) / 75;
553         cue->frame = (lba % (60*75)) % 75;
554 }
555
556 static void
557 cleanup(int dummy __unused)
558 {
559         if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0) 
560                 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
561 }
562
563 static void
564 usage(void)
565 {
566         fprintf(stderr,
567             "Usage: %s [-delmnpqtv] [-f device] [-s speed] [command]"
568             " [command file ...]\n", getprogname());
569         exit(EX_USAGE);
570 }