source: ubiquity/trunk/fuentes/d-i/source/partman-base/parted_server.c @ 3069

Last change on this file since 3069 was 3069, checked in by kbut, 3 years ago

Update from upstream

File size: 88.6 KB
Line 
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
4
5#include <parted/parted.h>
6#include <sys/types.h>
7#include <sys/wait.h>
8#include <sys/stat.h>
9#include <unistd.h>
10#include <stdio.h>
11#include <string.h>
12#include <fcntl.h>
13#include <errno.h>
14#include <stdbool.h>
15#include <ctype.h>
16#include <signal.h>
17#include <stdarg.h>
18
19/**********************************************************************
20   Logging
21**********************************************************************/
22
23/* This file is used as pid-file. */
24char pidfile_name[] = "/var/run/parted_server.pid";
25
26/* These are the communication fifos */
27char infifo_name[] = "/var/lib/partman/infifo";
28char outfifo_name[] = "/var/lib/partman/outfifo";
29char stopfifo_name[] = "/var/lib/partman/stopfifo";
30
31/* This file is used as log-file. */
32char logfile_name[] = "/var/log/partman";
33
34/* main() opens the logfile */
35FILE *logfile;
36
37/* This string is used to prepend the messages written in the log file */
38char const program_name[] = "parted_server";
39
40/* Write a message to the log-file.  Arguments are the same as in printf.
41 * Note that this deliberately uses asprintf, not xasprintf; if it fails,
42 * there's nothing useful we can do, and we might be about to exit anyway.
43 */
44/* log(const char *format, ...) */
45#define log(...) \
46        ({ \
47                char *msg_log; \
48                if (asprintf(&msg_log, __VA_ARGS__) >= 0) { \
49                        fprintf(logfile, "%s: %s\n", program_name, msg_log); \
50                        fflush(logfile); \
51                        free(msg_log); \
52                } \
53        })
54
55/* Write a line to the log-file and exit. */
56/* critical_error(const char *format, ...) */
57#define critical_error(...) \
58        ({ \
59                log(__VA_ARGS__); \
60                log("Line %i. CRITICAL ERROR!!!  EXITING.", __LINE__); \
61                exit(1); \
62        })
63
64/* For debugging purposes */
65#define traceline() log("Line: %i", __LINE__)
66
67#define assert(x) \
68        if(!(x)) \
69                critical_error("Assertion failed at line %i.", __LINE__)
70
71#define log_partitions(dev, disk) \
72        (dump_info(logfile, dev, disk), fflush(logfile))
73
74char *
75xasprintf(const char *format, ...)
76{
77        va_list args;
78        char *result;
79
80        va_start(args, format);
81        if (vasprintf(&result, format, args) < 0) {
82                if (errno == ENOMEM)
83                        critical_error("Cannot allocate memory.");
84                return NULL;
85        }
86
87        return result;
88}
89
90enum alignment {
91        ALIGNMENT_CYLINDER,
92        ALIGNMENT_MINIMAL,
93        ALIGNMENT_OPTIMAL
94} alignment = ALIGNMENT_OPTIMAL;
95
96/**********************************************************************
97   Reading from infifo and writing to outfifo
98**********************************************************************/
99
100/* This directory contains infifo and outfifo */
101char my_directory[] = "/var/lib/partman";
102
103/* The output FIFO.  We write to it, the clients read. */
104FILE *outfifo = NULL;
105
106/* Open the output FIFO.  After this function the global variable
107   outfifo can be used for writing. */
108void
109open_out()
110{
111        char *str;
112        log("Opening outfifo");
113        str = xasprintf("%s/outfifo", my_directory);
114        outfifo = fopen(str, "w");
115        if (outfifo == NULL)
116                critical_error("Can't open outfifo");
117        free(str);
118}
119
120/* Write to the output FIFO.  The arguments are the same as in
121   printf. */
122#define oprintf(...) \
123        ({ \
124                char *msg_oprintf; \
125                fprintf(outfifo,__VA_ARGS__); \
126                fflush(outfifo); \
127                msg_oprintf = xasprintf(__VA_ARGS__); \
128                log("OUT: %s\n", msg_oprintf); \
129                free(msg_oprintf); \
130        })
131
132/* The input FIFO.  We read from it, the clients write. */
133FILE *infifo = NULL;
134
135/* Open the input FIFO.  After this function the global variable
136   infifo can be used for reading */
137void
138open_in()
139{
140        char *str;
141        log("Opening infifo");
142        str = xasprintf("%s/infifo", my_directory);
143        infifo = fopen(str, "r");
144        if (infifo == NULL)
145                critical_error("Can't open infifo");
146        free(str);
147}
148
149/* Do fscanf from the input FIFO.  The arguments are the same as in
150   the function `scanf' */
151#define iscanf(...) fscanf(infifo,__VA_ARGS__)
152
153/* Read the remainder of this line from the input FIFO, skipping leading
154 * whitespace. Sets *str to NULL if there was no data left in the FIFO (as
155 * opposed to merely optional leading whitespace followed by a newline,
156 * indicating an empty argument following the whitespace; in that case, set
157 * *str to the empty string). Caller is expected to free *str.
158 */
159void
160iscan_line(char **str, int expect_leading_newline)
161{
162        int c;
163
164        *str = NULL;
165
166        c = fgetc(infifo);
167        if (c == EOF)
168                return;
169        if (c == '\n' && expect_leading_newline) {
170                c = fgetc(infifo);
171                if (c == EOF)
172                        return;
173        }
174        while (c != EOF && c != '\n') {
175                if (isspace((unsigned char) c))
176                        c = fgetc(infifo);
177                else {
178                        ungetc(c, infifo);
179                        break;
180                }
181        }
182
183        if (c == EOF || c == '\n')
184                *str = calloc(1, 1);
185        else
186                iscanf("%a[^\n]", str);
187}
188
189void
190synchronise_with_client()
191{
192        char *str;
193        FILE *stopfifo;
194        str = xasprintf("%s/stopfifo", my_directory);
195        stopfifo = fopen(str, "r");
196        if (stopfifo == NULL)
197                critical_error("Can't open stopfifo for synchronisation");
198        free(str);
199        fclose(stopfifo);
200}
201
202/* This function closes infifo and outfifo.  Then in order to
203   synchronise with the clients it opens and closes first outfifo and
204   afterwards infifo but in oposite direction -- outfifo for reading
205   and infifo for writing. */
206void
207close_fifos_and_synchronise()
208{
209        char *str;
210        int c;
211        log("Closing infifo and outfifo");
212        fclose(infifo);
213        fclose(outfifo);
214        synchronise_with_client();
215        str = xasprintf("%s/outfifo", my_directory);
216        outfifo = fopen(str, "r");
217        if (outfifo == NULL)
218                critical_error("Can't open outfifo for synchronisation");
219        free(str);
220        while (EOF != (c = fgetc(outfifo))) {
221        }
222        fclose(outfifo);
223        synchronise_with_client();
224        str = xasprintf("%s/infifo", my_directory);
225        infifo = fopen(str, "w");
226        if (infifo == NULL)
227                critical_error("Can't open infifo for synchronisation");
228        free(str);
229        fclose(infifo);
230        synchronise_with_client();
231}
232
233/**********************************************************************
234   Timer
235**********************************************************************/
236
237bool timer_started = false;
238
239/* Tell the client to open a progress bar. */
240void
241start_timer()
242{
243        assert(!timer_started);
244        oprintf("Timer\n");
245        timer_started = true;
246}
247
248/* Tell the client to close the progress bar. */
249void
250stop_timer()
251{
252        assert(timer_started);
253        oprintf("ready\n");
254        timer_started = false;
255}
256
257/* Tell the client the fraction of operation done (in permiles). */
258void
259timer_handler(PedTimer *timer, void *context)
260{
261        assert(timer_started);
262        oprintf("%.0f %s\n", 1000 * timer->frac, timer->state_name);
263}
264
265/* Like ped_file_system_resize but automaticaly creates PedTimer */
266int
267timered_file_system_resize(PedFileSystem *fs, PedGeometry *geom)
268{
269        int result;
270        PedTimer *timer;
271        start_timer();
272        timer = ped_timer_new(&timer_handler, NULL);
273        result = ped_file_system_resize(fs, geom, timer);
274        stop_timer();
275        ped_timer_destroy(timer);
276        return result;
277}
278
279/**********************************************************************
280   Exception handler
281**********************************************************************/
282
283/* Generate for the client an exception using the following scenario: */
284/*    1. Print `type' in outfifo */
285/*    2. Print `message' to be presented to the user. */
286/*    3. Print newline to mark the end of the message. */
287/*    4. Print the options for the user, one per line and end with an
288 *       empty line. */
289/*    5. Read from infifo the user response.  This is either
290 *       "unhandled" or one of the options from 4. */
291/* Arguments: `type' is a string such as "information", "warning",
292 * "error", etc., `message' is the text to be presented to the user
293 * and `options' is an array of pointers to strings such "Yes", "No",
294 * "Cancel", etc.; the last pointer is NULL.  The function returns the
295 * index of the option chosen by the user or -1 if the option read in
296 * 5. was "unhandled".  The client responses with "unhandled" when the
297 * user cancels the debconf dialog or when the dialog was not
298 * presented to the user because of the debconf priority. */
299int
300pseudo_exception(char *type, char *message, char **options)
301{
302        int i;
303        char *str;
304        bool timer_was_started = timer_started;
305        if (timer_was_started)
306                stop_timer();
307        oprintf("%s\n", type);
308        oprintf("%s\n", message);
309        oprintf("\n");
310        for (i = 0; options[i] != NULL; i++) {
311                oprintf("%s\n", options[i]);
312        }
313        oprintf("\n");
314        if (timer_was_started)
315                start_timer();
316        iscan_line(&str, 1);
317        if (!str)
318                critical_error("No data in infifo.");
319        if (!strcmp(str, "unhandled")) {
320                log("User canceled exception handler");
321                return -1;
322        }
323        for (i = 0; options[i] != NULL; i++)
324                if (!strcasecmp(str, options[i])) {
325                        free(str);
326                        return i;
327                }
328        critical_error("exception_handler: Bad option: \"%s\"", str);
329}
330
331/* The maximal meaningful bit in PedExceptionOption.  In the current
332   version of libparted (1.6) this is 7, but let us be safer. */
333#define MAXIMAL_OPTION 10
334#define POWER_MAXIMAL_OPTION 1024       /* 2 to the MAXIMAL_OPTION */
335
336/* The exception handler for ped_exception_set_handler(). */
337PedExceptionOption
338exception_handler(PedException *ex)
339{
340        char *options[MAXIMAL_OPTION + 1];
341        int i;
342        unsigned bit;
343        int response;
344        i = 0;
345        for (bit = 1; bit <= POWER_MAXIMAL_OPTION; bit = bit << 1) {
346                if (bit & ex->options) {
347                        options[i] = ped_exception_get_option_string(bit);
348                        i++;
349                }
350        }
351        options[i] = NULL;
352        response =
353            pseudo_exception(ped_exception_get_type_string(ex->type),
354                             ex->message, options);
355        if (response == -1) {
356                log("User canceled exception handler");
357                return PED_EXCEPTION_UNHANDLED;
358        }
359        for (bit = 1; bit <= POWER_MAXIMAL_OPTION; bit = bit << 1) {
360                if (bit & ex->options) {
361                        char *option;
362                        option = ped_exception_get_option_string(bit);
363                        if (!strcasecmp(options[response], option)) {
364                                return bit;
365                        }
366                }
367        }
368        critical_error("exception_handler: Bad option: <%s>",
369                       options[response]);
370}
371
372/* If we want to temporarily disable the exception handler for some
373   commands, we use deactivate_exception_handler() before them and
374   activate_exception_handler after them. */
375
376unsigned handler_deactivation_counter = 0;
377
378void
379deactivate_exception_handler()
380{
381        if (handler_deactivation_counter == 0)
382                ped_exception_fetch_all();
383        handler_deactivation_counter++;
384}
385
386void
387activate_exception_handler()
388{
389        assert(handler_deactivation_counter > 0);
390        handler_deactivation_counter--;
391        if (handler_deactivation_counter == 0)
392                ped_exception_leave_all();
393}
394
395/**********************************************************************
396   Registry of the opened devices
397**********************************************************************/
398
399struct devdisk {
400        char *name;
401        PedDevice *dev;
402        PedDisk *disk;
403        bool changed;
404        PedGeometry *geometries;
405        int number_geometries;
406        enum alignment alignment;
407};
408
409/* We store the accessed devices from `devices[0]' to
410   `devices[number_devices - 1]'.  `number_devices' is a small number
411   so there is no need to use a hash table or some more advanced data
412   structure.  Moreover a version of parted_server using the hash
413   implementation from libdebian-installer was 200 bytes longer. */
414
415unsigned number_devices = 0;
416struct devdisk *devices = NULL;
417
418/* The size of the array `devices' */
419unsigned allocated_devices = 0;
420
421/* index = index_of_name(name);
422 * 0 == strcmp(devices[index].name, name)
423 *
424 * Be careful not to write code like devices[index_of_name(name)].
425 * This function may change devices, so a sequence point is required.
426 */
427int
428index_of_name(const char *name)
429{
430        int i;
431        assert(name != NULL);
432        for (i = 0; i < number_devices; i++)
433                if (0 == strcmp(name, devices[i].name))
434                        return i;
435        if (number_devices == allocated_devices) {
436                allocated_devices = 1 + 2 * allocated_devices;
437                devices = realloc(devices,
438                                  sizeof(struct devdisk[allocated_devices]));
439                if (devices == NULL)
440                        critical_error("Cannot allocate memory.");
441        }
442        number_devices++;
443        devices[i].name = strdup(name);
444        if (NULL == devices[i].name)
445                critical_error("Cannot allocate memory.");
446        devices[i].dev = NULL;
447        devices[i].disk = NULL;
448        devices[i].changed = false;
449        devices[i].geometries = NULL;
450        devices[i].number_geometries = 0;
451        devices[i].alignment = alignment;
452        return i;
453}
454
455int
456index_of_device(const PedDevice *dev)
457{
458        int i;
459        assert(dev != NULL);
460        for (i = 0; i < number_devices; i++)
461                if (dev == devices[i].dev)
462                        return i;
463        return -1;
464}
465
466/* Mangle fstype to abstract changes in parted code */
467void
468mangle_fstype_name(char **fstype)
469{
470        if (!strcasecmp(*fstype, "linux-swap")) {
471                free(*fstype);
472                *fstype = strdup("linux-swap(v1)");
473        }
474}
475
476/* Return the PedDevice of `name'. */
477PedDevice *
478device_named(const char *name)
479{
480        int index = index_of_name(name);
481        return devices[index].dev;
482}
483
484
485/* Return the PedDisk of `name'. */
486PedDisk *
487disk_named(const char *name)
488{
489        int index = index_of_name(name);
490        return devices[index].disk;
491}
492
493/* True iff the PedDevice of `name' is not NULL. */
494bool
495device_opened(const char *name)
496{
497        return NULL != device_named(name);
498}
499
500/* Set the PedDevice of `name' to be `dev'.  The old PedDevice of
501   `name' (if any) will be ped_device_destroy-ed. */
502void
503set_device_named(const char *name, PedDevice *dev)
504{
505        PedDevice *old_dev;
506        int index = index_of_name(name);
507        assert(disk_named(name) == NULL);
508        old_dev = device_named(name);
509        if (NULL != old_dev)
510                ped_device_destroy(old_dev);
511        devices[index].dev = dev;
512}
513
514void
515remember_geometries_named(const char *name)
516{
517        static unsigned const max_partition = 50;
518        PedGeometry *geometries;
519        PedDisk *disk;
520        int last;
521        PedPartition *part;
522        int index = index_of_name(name);
523        geometries = devices[index].geometries;
524        if (NULL != geometries)
525                free(geometries);
526        disk = disk_named(name);
527        if (disk == NULL) {
528                devices[index].geometries = NULL;
529                devices[index].number_geometries = 0;
530        } else {
531                geometries = malloc(sizeof(PedGeometry[max_partition]));
532                last = 0;
533                for (part = NULL;
534                     NULL != (part = ped_disk_next_partition(disk, part));) {
535                        if (PED_PARTITION_EXTENDED & part->type)
536                                continue;
537                        if (PED_PARTITION_METADATA & part->type)
538                                continue;
539                        if (PED_PARTITION_FREESPACE & part->type)
540                                continue;
541                        ped_geometry_init(geometries + last,
542                                          disk->dev,
543                                          part->geom.start, part->geom.length);
544                        last = last + 1;
545                        if (last >= max_partition)
546                                critical_error("Too many partitions");
547                }
548                geometries = realloc(geometries, sizeof(PedGeometry[last]));
549                if (last != 0 && geometries == NULL)
550                        critical_error("Cannot allocate memory");
551                devices[index].geometries = geometries;
552                devices[index].number_geometries = last;
553        }
554}
555
556/* Set the PedDisk of `name' to be `disk'.  The old PedDisk of `name'
557   (if any) will be ped_disk_destroy-ed. */
558void
559set_disk_named(const char *name, PedDisk *disk)
560{
561        PedDisk *old_disk;
562        int index = index_of_name(name);
563        assert(device_opened(name));
564        old_disk = disk_named(name);
565        if (NULL != old_disk)
566                ped_disk_destroy(old_disk);
567        devices[index].disk = disk;
568        if (disk) {
569                if (ped_disk_is_flag_available(disk,
570                                               PED_DISK_CYLINDER_ALIGNMENT))
571                        ped_disk_set_flag(disk, PED_DISK_CYLINDER_ALIGNMENT,
572                                          devices[index].alignment ==
573                                                ALIGNMENT_CYLINDER);
574                else if (0 != strcmp(disk->type->name, "gpt"))
575                        /* If the PED_DISK_CYLINDER_ALIGNMENT flag isn't
576                           available, then there are two alternatives:
577                           either the disk label format is too old to know
578                           about modern alignment (#579948), or it's too new
579                           to care about cylinder alignment (#674894).  The
580                           only format currently known to fall into the
581                           latter category is GPT; for the others, we should
582                           assume that *only* cylinder alignment is
583                           available. */
584                        devices[index].alignment = ALIGNMENT_CYLINDER;
585        }
586}
587
588/* True if the partition doesn't exist on the storage device */
589bool
590named_partition_is_virtual(const char *name, PedSector start, PedSector end)
591{
592        PedGeometry *geometries;
593        int i;
594        int last;
595        int index = index_of_name(name);
596        log("named_partition_is_virtual(%s,%lli,%lli)", name, start, end);
597        geometries = devices[index].geometries;
598        last = devices[index].number_geometries;
599        if (NULL == geometries) {
600                log("yes");
601                return true;
602        }
603        for (i = 0; i < last; i++) {
604                if (start == geometries[i].start && end == geometries[i].end) {
605                        log("no");
606                        return false;
607                }
608        }
609        log("yes");
610        return true;
611}
612
613/* True iff the partition table of `name' has been changed. */
614bool
615named_is_changed(const char *name)
616{
617        int index = index_of_name(name);
618        return devices[index].changed;
619}
620
621/* Note the partition table of `name' as having been changed. */
622void
623change_named(const char *name)
624{
625        int index = index_of_name(name);
626        log("Note %s as changed", name);
627        devices[index].changed = true;
628}
629
630/* Note the partition table of `name' as unchanged. */
631void
632unchange_named(const char *name)
633{
634        int index = index_of_name(name);
635        log("Note %s as unchanged", name);
636        devices[index].changed = false;
637        remember_geometries_named(name);
638}
639
640/* Return the desired alignment for dev. */
641enum alignment
642alignment_of_device(const PedDevice *dev)
643{
644        int index = index_of_device(dev);
645        if (index >= 0)
646                return devices[index].alignment;
647        else
648                return ALIGNMENT_CYLINDER;
649}
650
651
652/**********************************************************************
653   Partition creation
654**********************************************************************/
655
656/* True if `disk' has already an extended partition. */
657bool
658has_extended_partition(PedDisk *disk)
659{
660        assert(disk != NULL);
661        return ped_disk_extended_partition(disk) != NULL;
662}
663
664void
665set_alignment(void)
666{
667        const char *align_env = getenv("PARTMAN_ALIGNMENT");
668
669        if (align_env && !strcmp(align_env, "cylinder"))
670                alignment = ALIGNMENT_CYLINDER;
671        else if (align_env && !strcmp(align_env, "minimal"))
672                alignment = ALIGNMENT_MINIMAL;
673        else
674                alignment = ALIGNMENT_OPTIMAL;
675}
676
677/* Get a constraint suitable for partition creation on this disk. */
678PedConstraint *
679partition_creation_constraint(const PedDevice *cdev)
680{
681        PedSector md_grain_size;
682        PedConstraint *aligned, *gap_at_end, *combined;
683        PedGeometry gap_at_end_geom;
684        enum alignment cdev_alignment = alignment_of_device(cdev);
685
686        if (cdev_alignment == ALIGNMENT_OPTIMAL)
687                aligned = ped_device_get_optimal_aligned_constraint(cdev);
688        else if (cdev_alignment == ALIGNMENT_MINIMAL)
689                aligned = ped_device_get_minimal_aligned_constraint(cdev);
690        else
691                aligned = ped_device_get_constraint(cdev);
692        if (cdev->type == PED_DEVICE_DM)
693                return aligned;
694
695        /* We must ensure that there's a small gap at the end, since
696         * otherwise MD 0.90 metadata at the end of a partition may confuse
697         * mdadm into believing that both the disk and the partition
698         * represent the same RAID physical volume.  0.90 metadata is
699         * located by rounding the device size down to a 64K boundary and
700         * subtracting 64K (1.x metadata is either between 8K and 12K from
701         * the end, or at or near the start), so we round down to 64K and
702         * subtract one more sector.
703         */
704        md_grain_size = 65536 / cdev->sector_size;
705        if (md_grain_size == 0)
706                md_grain_size = 1;
707        ped_geometry_init(&gap_at_end_geom, cdev, 0,
708                          ped_round_down_to(cdev->length, md_grain_size) - 1);
709        gap_at_end = ped_constraint_new(ped_alignment_any, ped_alignment_any,
710                                        &gap_at_end_geom, &gap_at_end_geom,
711                                        1, cdev->length);
712
713        combined = ped_constraint_intersect(aligned, gap_at_end);
714
715        ped_constraint_destroy(gap_at_end);
716        ped_constraint_destroy(aligned);
717        return combined;
718}
719
720/* Add to `disk' a new extended partition starting at `start' and
721   ending at `end' */
722PedPartition *
723add_extended_partition(PedDisk *disk, PedSector start, PedSector end)
724{
725        PedPartition *extended;
726        assert(disk != NULL);
727        assert(!has_extended_partition(disk));
728        /* ext2 has no sense, but parted requires some argument */
729        extended = ped_partition_new(disk, PED_PARTITION_EXTENDED,
730                                     ped_file_system_type_get("ext2"),
731                                     start, end);
732        if (!extended) {
733                return NULL;
734        }
735        if (!ped_disk_add_partition(disk, extended,
736                                    ped_constraint_any(disk->dev))) {
737                ped_partition_destroy(extended);
738                return NULL;
739        }
740        return extended;
741}
742
743/* Makes the extended partition as large as possible. */
744void
745maximize_extended_partition(PedDisk *disk)
746{
747        PedPartition *extended;
748        assert(disk != NULL);
749        assert(has_extended_partition(disk));
750        extended = ped_disk_extended_partition(disk);
751        ped_disk_maximize_partition(disk, extended,
752                                    ped_constraint_any(disk->dev));
753}
754
755/* Makes the extended partition as small as possible or removes it if
756   there are no logical partitions. */
757void
758minimize_extended_partition(PedDisk *disk)
759{
760        assert(disk != NULL);
761        if (0 != strcmp(disk->type->name, "dvh"))
762                ped_disk_minimize_extended_partition(disk);
763}
764
765/* Add to `disk' a new primary partition with file system `fs_type'
766   starting at `start' and ending at `end'.  Note: The partition is
767   not formatted, but only created. */
768PedPartition *
769add_primary_partition(PedDisk *disk, PedFileSystemType *fs_type,
770                      PedSector start, PedSector end)
771{
772        PedPartition *part;
773        assert(disk != NULL);
774        log("add_primary_partition(disk(%lli),%lli-%lli)",
775            disk->dev->length, start, end);
776        if (has_extended_partition(disk)) {
777                /* Minimise the extended partition.  If there is an
778                   extended partition, but no logical partitions, this
779                   command removes the extended partition. */
780                log("Minimizing extended partition.");
781                minimize_extended_partition(disk);
782        }
783        part = ped_partition_new(disk, 0, fs_type, start, end);
784        if (part == NULL) {
785                log("Cannot create new primary partition.");
786                return NULL;
787        }
788        if (!ped_disk_add_partition(disk, part, partition_creation_constraint(disk->dev))) {
789                log("Cannot add the primary partition to partition table.");
790                ped_partition_destroy(part);
791                return NULL;
792        }
793        return part;
794}
795
796/* Add to `disk' a new logical partition with file system `fs_type'
797   starting at `start' and ending at `end'.  Note: The partition is
798   not formatted, but only created. */
799PedPartition *
800add_logical_partition(PedDisk *disk, PedFileSystemType *fs_type,
801                      PedSector start, PedSector end)
802{
803        PedPartition *part;
804        assert(disk != NULL && fs_type != NULL);
805        if (!has_extended_partition(disk))
806                if (!add_extended_partition(disk, start, end))
807                        return NULL;
808        maximize_extended_partition(disk);
809        part = ped_partition_new(disk, PED_PARTITION_LOGICAL, fs_type,
810                                 start, end);
811        if (part == NULL) {
812                minimize_extended_partition(disk);
813                return NULL;
814        }
815        if (!ped_disk_add_partition(disk, part, partition_creation_constraint(disk->dev))) {
816                ped_partition_destroy(part);
817                minimize_extended_partition(disk);
818                return NULL;
819        }
820        minimize_extended_partition(disk);
821        return part;
822}
823
824/* Resizes `part' from `disk' to start from `start' and end at `end'.
825   If `open_filesystem' is true and `disk' contains some file system
826   then it is also resized.  Returns true on success. */
827bool
828resize_partition(PedDisk *disk, PedPartition *part,
829                 PedSector start, PedSector end, bool open_filesystem)
830{
831        PedFileSystem *fs;
832        PedConstraint *constraint;
833        PedSector old_start, old_end;
834        bool result;
835        log("resize_partition(openfs=%s)", open_filesystem ? "true" : "false");
836        old_start = (part->geom).start;
837        old_end = (part->geom).end;
838        if (old_start == start && old_end == end)
839                return true;
840        if (open_filesystem) {
841                deactivate_exception_handler();
842                fs = ped_file_system_open(&(part->geom));
843                activate_exception_handler();
844                log("opened file system: %s", NULL != fs ? "yes" : "no");
845                if (NULL != fs && (fs->geom->start < (part->geom).start
846                                   || fs->geom->end > (part->geom).end)) {
847                        ped_file_system_close(fs);
848                        fs = NULL;
849                }
850                if (NULL == fs && NULL != ped_file_system_probe(&(part->geom)))
851                        return false;
852                if (NULL != fs)
853                        constraint = ped_file_system_get_resize_constraint(fs);
854                else
855                        constraint = ped_constraint_any(disk->dev);
856        } else {
857                PedFileSystemType *fs_type;
858                PedGeometry *fs_geom;
859                PedAlignment start_align;
860                PedGeometry full_dev;
861                fs = NULL;
862                fs_type = ped_file_system_probe(&(part->geom));
863                log("probed file system: %s", NULL != fs_type ? "yes" : "no");
864                if (NULL != fs_type)
865                        fs_geom = ped_file_system_probe_specific(fs_type,
866                                                                 &part->geom);
867                else
868                        fs_geom = NULL;
869                if (NULL != fs_geom && (fs_geom->start < (part->geom).start
870                                        || fs_geom->end > (part->geom).end)) {
871                        log("broken filesystem detected");
872                        ped_geometry_destroy(fs_geom);
873                        fs_geom = NULL;
874                }
875                if (NULL == fs_geom && NULL != fs_type)
876                        return false;
877                if (NULL != fs_geom) {
878                        /* We cannot resize or move the fs but we can
879                         * move the end of the partition so long as it
880                         * contains the whole fs.
881                         */
882                        if (ped_alignment_init(&start_align, fs_geom->start, 0)
883                            && ped_geometry_init(&full_dev, disk->dev,
884                                                 0, disk->dev->length - 1)) {
885                                constraint = ped_constraint_new(
886                                        &start_align,
887                                        ped_alignment_any,
888                                        &full_dev, &full_dev,
889                                        fs_geom->length,
890                                        disk->dev->length);
891                        } else {
892                                constraint = NULL;
893                        }
894                        ped_geometry_destroy(fs_geom);
895                } else {
896                        constraint = ped_constraint_any(disk->dev);
897                }
898        }
899        if (NULL == constraint) {
900                log("failed to get resize constraint");
901                if (NULL != fs)
902                        ped_file_system_close(fs);
903                return false;
904        }
905        if (part->type & PED_PARTITION_LOGICAL)
906                maximize_extended_partition(disk);
907        if (!ped_disk_set_partition_geom(disk, part, constraint, start, end))
908                result = false;
909        else if (NULL == fs)
910                result = true;
911        else if (timered_file_system_resize(fs, &(part->geom))) {
912                result = true;
913        } else {
914                ped_disk_set_partition_geom(disk, part,
915                                            ped_constraint_any(disk->dev),
916                                            old_start, old_end);
917                result = false;
918        }
919        if (fs != NULL)
920                ped_file_system_close(fs);
921        if (part->type & PED_PARTITION_LOGICAL)
922                minimize_extended_partition(disk);
923        return result;
924        /* TODO: not sure if constraints here should be
925           ped_constraint_destroy-ed.  Let's be safe. */
926}
927
928/**********************************************************************
929   Getting info
930**********************************************************************/
931
932/* true when it is possible to create a primary partition in `space'.
933   `space' must be a free space in `disk'. */
934bool
935possible_primary_partition(PedDisk *disk, PedPartition *space)
936{
937        bool result;
938        assert(disk != NULL);
939        assert(space != NULL && PED_PARTITION_FREESPACE & space->type);
940        deactivate_exception_handler();
941        result = (!(PED_PARTITION_LOGICAL & space->type)
942                  && (ped_disk_get_primary_partition_count(disk)
943                      < ped_disk_get_max_primary_partition_count(disk)));
944        activate_exception_handler();
945        return result;
946}
947
948/* true when it is possible to create an extended partition in `space'.
949   `space' must be a free space in `disk'. */
950bool
951possible_extended_partition(PedDisk *disk, PedPartition *space)
952{
953        bool result;
954        assert(disk != NULL);
955        assert(space != NULL && PED_PARTITION_FREESPACE & space->type);
956        deactivate_exception_handler();
957        result = (ped_disk_type_check_feature(disk->type,
958                                              PED_DISK_TYPE_EXTENDED)
959                  && !has_extended_partition(disk)
960#ifdef __s390__
961                  /* DASD drives can only do 3 partitions */
962                  && strcmp(disk->dev->model, "IBM S390 DASD drive")
963#endif
964                  && possible_primary_partition(disk, space));
965        activate_exception_handler();
966        return result;
967}
968
969/* true if the last sector of `part1' is phisicaly before the first
970   sector of `part2'. */
971inline bool
972partition_before(PedPartition *part1, PedPartition *part2)
973{
974        return (part1->geom).end < (part2->geom).start;
975}
976
977/* true when it is possible to create a logical partition in `space'.
978   `space' must be a free space in `disk'. */
979bool
980possible_logical_partition(PedDisk *disk, PedPartition *space)
981{
982        PedPartition *extended, *part;
983        bool result;
984        assert(disk != NULL);
985        assert(space != NULL && (PED_PARTITION_FREESPACE & space->type));
986        deactivate_exception_handler();
987        if (!has_extended_partition(disk))
988                result = possible_extended_partition(disk, space);
989        else {
990                extended = ped_disk_extended_partition(disk);
991                result = true;
992                part = ped_disk_next_partition(disk, NULL);
993                while (result && NULL != part) {
994                        if (ped_partition_is_active(part)
995                            && ((partition_before(space, part)
996                                 && partition_before(part, extended))
997                                || (partition_before(extended, part)
998                                    && partition_before(part, space)))) {
999                                /* There is a primary partition between us
1000                                   and the extended partition. */
1001                                assert(!(PED_PARTITION_LOGICAL & part->type));
1002                                result = false;
1003                        }
1004                        part = ped_disk_next_partition(disk, part);
1005                }
1006        }
1007        activate_exception_handler();
1008        return result;
1009}
1010
1011/* Finds in `disk' a partition with id `id' and returns it. */
1012PedPartition *
1013partition_with_id(PedDisk *disk, char *id)
1014{
1015        PedPartition *part;
1016        long long start, end;
1017        long long start_sector, end_sector;
1018        assert(id != NULL);
1019        log("partition_with_id(%s)", id);
1020        if (2 != sscanf(id, "%lli-%lli", &start, &end))
1021                critical_error("Bad id %s", id);
1022        start_sector = start / disk->dev->sector_size;
1023        end_sector = (end - disk->dev->sector_size + 1) / disk->dev->sector_size;
1024        if (disk == NULL)
1025                return NULL;
1026        for (part = NULL;
1027             NULL != (part = ped_disk_next_partition(disk, part));)
1028                if ((part->geom).start == start_sector
1029                    && (part->geom).end == end_sector)
1030                        return part;
1031        return NULL;
1032}
1033
1034/* Returns informational string about `part' from `disk'.  Format:*/
1035/* Number<TAB>id<TAB>length<TAB>type<TAB>fs<TAB>path<TAB>name */
1036char *
1037partition_info(PedDisk *disk, PedPartition *part)
1038{
1039        char const *type;
1040        char const *fs;
1041        char *path;
1042        char const *name;
1043        char *result;
1044        assert(disk != NULL && part != NULL);
1045        if (PED_PARTITION_FREESPACE & part->type) {
1046                bool possible_primary = possible_primary_partition(disk, part);
1047                bool possible_logical = possible_logical_partition(disk, part);
1048                if (possible_primary)
1049                        if (possible_logical)
1050                                type = "pri/log";
1051                        else
1052                                type = "primary";
1053                else if (possible_logical)
1054                        type = "logical";
1055                else
1056                        type = "unusable";
1057        } else if (PED_PARTITION_LOGICAL & part->type)
1058                type = "logical";
1059        else
1060                type = "primary";
1061
1062        if (PED_PARTITION_FREESPACE & part->type)
1063                fs = "free";
1064        else if (PED_PARTITION_METADATA & part->type)
1065                fs = "label";
1066        else if (PED_PARTITION_EXTENDED & part->type)
1067                fs = "extended";
1068        else if (NULL == (part->fs_type))
1069                fs = "unknown";
1070        else if (0 == strncmp(part->fs_type->name, "linux-swap", 10))
1071                fs = "linux-swap";
1072        else
1073                fs = part->fs_type->name;
1074
1075        if (0 == strcmp(disk->type->name, "loop")) {
1076                path = strdup(disk->dev->path);
1077/*         } else if (0 == strcmp(disk->type->name, "dvh")) { */
1078/*                 PedPartition *p; */
1079/*                 int count = 1; */
1080/*                 int number_offset; */
1081/*                 for (p = NULL; */
1082/*                      NULL != (p = ped_disk_next_partition(disk, p));) { */
1083/*                         if (PED_PARTITION_METADATA & p->type) */
1084/*                                 continue; */
1085/*                         if (PED_PARTITION_FREESPACE & p->type) */
1086/*                                 continue; */
1087/*                         if (PED_PARTITION_LOGICAL & p->type) */
1088/*                                 continue; */
1089/*                         if (part->num > p->num) */
1090/*                                 count++; */
1091/*                 } */
1092/*                 path = ped_partition_get_path(part); */
1093/*                 number_offset = strlen(path); */
1094/*                 while (number_offset > 0 && isdigit(path[number_offset-1])) */
1095/*                         number_offset--; */
1096/*                 sprintf(path + number_offset, "%i", count); */
1097        } else {
1098                path = ped_partition_get_path(part);
1099        }
1100        if (ped_disk_type_check_feature(part->disk->type,
1101                                        PED_DISK_TYPE_PARTITION_NAME)
1102            && ped_partition_is_active(part))
1103                name = ped_partition_get_name(part);
1104        else
1105                name = "";
1106        result = xasprintf("%i\t%lli-%lli\t%lli\t%s\t%s\t%s\t%s",
1107                           part->num,
1108                           (part->geom).start * disk->dev->sector_size,
1109                           (part->geom).end * disk->dev->sector_size + disk->dev->sector_size - 1,
1110                           (part->geom).length * disk->dev->sector_size, type, fs, path, name);
1111        free(path);
1112        return result;
1113}
1114
1115/* Print in `dumpfile' information about the `dev', `disk' and the
1116   partitions in `disk'. */
1117void
1118dump_info(FILE *dumpfile, PedDevice *dev, PedDisk *disk)
1119{
1120        PedPartition *part;
1121        deactivate_exception_handler();
1122        if (dev == NULL) {
1123                fprintf(dumpfile, "Device: no");
1124                activate_exception_handler();
1125                return;
1126        }
1127        fprintf(dumpfile, "Device: yes\n");
1128        fprintf(dumpfile, "Model: %s\n", dev->model);
1129        fprintf(dumpfile, "Path: %s\n", dev->path);
1130        fprintf(dumpfile, "Sector size: %lli\n", dev->sector_size);
1131        fprintf(dumpfile, "Sectors: %lli\n", dev->length);
1132        fprintf(dumpfile, "Sectors/track: %i\n", dev->bios_geom.sectors);
1133        fprintf(dumpfile, "Heads: %i\n", dev->bios_geom.heads);
1134        fprintf(dumpfile, "Cylinders: %i\n", dev->bios_geom.cylinders);
1135        if (disk == NULL) {
1136                fprintf(dumpfile, "Partition table: no\n");
1137                activate_exception_handler();
1138                return;
1139        }
1140        fprintf(dumpfile, "Partition table: yes\n");
1141        fprintf(dumpfile, "Type: %s\n", disk->type->name);
1142        fprintf(dumpfile, "Partitions: #\tid\tlength\ttype\tfs\tpath\tname\n");
1143        for (part = NULL;
1144             NULL != (part = ped_disk_next_partition(disk, part));) {
1145                /* TODO: there is the same code in command_get_chs */
1146                long long cylinder_size, track_size;
1147                long long start, end;
1148                long long cyl_start, cyl_end;
1149                long long head_start, head_end;
1150                long long sec_start, sec_end;
1151                char *part_info = partition_info(disk, part);
1152                track_size = dev->bios_geom.sectors;
1153                cylinder_size = track_size * dev->bios_geom.heads;
1154                start = (part->geom).start;
1155                end = (part->geom).end;
1156                cyl_start = start / cylinder_size;
1157                cyl_end = end / cylinder_size;
1158                start = start % cylinder_size;
1159                end = end % cylinder_size;
1160                head_start = start / track_size;
1161                head_end = end / track_size;
1162                sec_start = start % track_size;
1163                sec_end = end % track_size;
1164                fprintf(dumpfile,
1165                        "(%lli,%lli,%lli)\t(%lli,%lli,%lli)\t%s\n",
1166                        cyl_start, head_start, sec_start, cyl_end,
1167                        head_end, sec_end, part_info);
1168                free(part_info);
1169        }
1170        fprintf(dumpfile, "Dump finished.\n");
1171        activate_exception_handler();
1172}
1173
1174/**********************************************************************
1175   Commands
1176*********************************************************************/
1177
1178PedDevice *dev;
1179PedDisk *disk;
1180char *device_name;
1181
1182void
1183scan_device_name()
1184{
1185        if (device_name != NULL)
1186                free(device_name);
1187        if (1 != iscanf("%as", &device_name))
1188                critical_error("Expected device identifier.");
1189        dev = device_named(device_name);
1190        disk = disk_named(device_name);
1191}
1192
1193void
1194command_quit()
1195{
1196        log("Quitting");
1197        fflush(logfile);
1198        exit(0);
1199}
1200
1201void
1202command_open()
1203{
1204        log("command_open()");
1205        char *device;
1206        scan_device_name();
1207        if (1 != iscanf("%as", &device))
1208                critical_error("Expected device name.");
1209        log("Request to open %s", device_name);
1210        open_out();
1211        if (device_opened(device_name)) {
1212                static char *only_ok[] = { "OK", NULL };
1213                log("Warning: the device is already opened");
1214                pseudo_exception("Warning",
1215                                 "The device is already opened.", only_ok);
1216        } else {
1217                set_device_named(device_name, ped_device_get(device));
1218        }
1219        oprintf("OK\n");
1220        if (NULL != device_named(device_name)) {
1221                oprintf("OK\n");
1222                deactivate_exception_handler();
1223                set_disk_named(device_name,
1224                               ped_disk_new(device_named(device_name)));
1225                unchange_named(device_name);
1226                activate_exception_handler();
1227        } else
1228                oprintf("failed\n");
1229        free(device);
1230}
1231
1232void
1233command_close()
1234{
1235        log("command_close()");
1236        scan_device_name();
1237        open_out();
1238        if (!device_opened(device_name)) {
1239                static char *only_cancel[] = { "Cancel", NULL };
1240                pseudo_exception("Error",
1241                                 "The device is not opened!", only_cancel);
1242        }
1243        set_disk_named(device_name, NULL);
1244        set_device_named(device_name, NULL);
1245        oprintf("OK\n");
1246}
1247
1248void
1249command_opened()
1250{
1251        log("command_opened()");
1252        scan_device_name();
1253        open_out();
1254        oprintf("OK\n");
1255        if (NULL != device_named(device_name)) {
1256                oprintf("yes\n");
1257        } else {
1258                oprintf("no\n");
1259        }
1260}
1261
1262void
1263command_virtual()
1264{
1265        char *id;
1266        PedPartition *part;
1267        scan_device_name();
1268        if (dev == NULL)
1269                critical_error("The device %s is not opened.", device_name);
1270        log("command_virtual()");
1271        open_out();
1272        if (1 != iscanf("%as", &id))
1273                critical_error("Expected partition id");
1274        log("is virtual partition with id %s", id);
1275        part = partition_with_id(disk, id);
1276        oprintf("OK\n");
1277        if (named_partition_is_virtual(device_name,
1278                                       part->geom.start, part->geom.end)) {
1279                oprintf("yes\n");
1280        } else {
1281                oprintf("no\n");
1282        }
1283        free(id);
1284}
1285
1286void
1287command_disk_unchanged()
1288{
1289        scan_device_name();
1290        if (dev == NULL)
1291                critical_error("The device %s is not opened.", device_name);
1292        log("command_disk_unchanged(%s)", device_name);
1293        open_out();
1294        oprintf("OK\n");
1295        unchange_named(device_name);
1296}
1297
1298void
1299command_is_changed()
1300{
1301        scan_device_name();
1302        if (dev == NULL)
1303                critical_error("The device %s is not opened.", device_name);
1304        log("command_is_changed(%s)", device_name);
1305        open_out();
1306        oprintf("OK\n");
1307        if (named_is_changed(device_name))
1308                oprintf("yes\n");
1309        else
1310                oprintf("no\n");
1311}
1312
1313/* Print in /var/log/partition_dump information about the disk, the
1314   partition table and the partitions. */
1315void
1316command_dump()
1317{
1318        FILE *dumpfile;
1319        static char *only_cancel[] = { "Cancel", NULL };
1320        scan_device_name();
1321        if (dev == NULL)
1322                critical_error("The device %s is not opened.", device_name);
1323        log("command_dump()");
1324        open_out();
1325        dumpfile = fopen("/var/log/partition_dump", "a+");
1326        if (dumpfile == NULL) {
1327                pseudo_exception("Error",
1328                                 "Can't open /var/log/partition_dump",
1329                                 only_cancel);
1330        } else {
1331                dump_info(dumpfile, dev, disk);
1332                fclose(dumpfile);
1333        }
1334        oprintf("OK\n");
1335}
1336
1337/* Check whether we are running on a sunxi-based, freescale-based, or
1338   AM33XX (beaglebone black) system. */
1339int
1340is_system_with_firmware_on_disk()
1341{
1342        int cpuinfo_handle;
1343        int result = 0;
1344        char buf[4096];
1345        int length;
1346
1347        if ((cpuinfo_handle = open("/proc/cpuinfo", O_RDONLY)) != -1) {
1348                length = read(cpuinfo_handle, buf, sizeof(buf)-1);
1349                if (length > 0) {
1350                        buf[length]='\0';
1351                        if (strstr(buf, "Allwinner") != NULL)
1352                                result = 1;
1353                        else if (strstr(buf, "Freescale") != NULL)
1354                                result = 1;
1355                        else if (strstr(buf, "AM33XX") != NULL)
1356                                result = 1;
1357                }
1358                close(cpuinfo_handle);
1359        }
1360        return result;
1361}
1362
1363void
1364command_commit()
1365{
1366        scan_device_name();
1367        if (dev == NULL)
1368                critical_error("The device %s is not opened.", device_name);
1369        log("command_commit()");
1370
1371        /* The boot device on sunxi-based systems needs special handling.
1372         * By default partman calls ped_disk_clobber when writing the
1373         * partition table, but on sunxi-based systems this would overwrite
1374         * the firmware area, resulting in an unbootable system (see
1375         * bug #751704).
1376         */
1377        if (is_system_with_firmware_on_disk() && !strcmp(disk->dev->path, "/dev/mmcblk0")) {
1378                disk->needs_clobber = 0;
1379                log("Sunxi/Freescale/AM33XX detected. Disabling ped_disk_clobber" \
1380                    "for the boot device %s to protect the firmware " \
1381                    "area.", disk->dev->path);
1382        }
1383
1384        open_out();
1385        if (disk != NULL && named_is_changed(device_name))
1386                ped_disk_commit(disk);
1387        unchange_named(device_name);
1388        oprintf("OK\n");
1389}
1390
1391void
1392command_undo()
1393{
1394        scan_device_name();
1395        if (dev == NULL)
1396                critical_error("The device %s is not opened.", device_name);
1397        log("command_undo()");
1398        open_out();
1399        log("Rereading disk label");
1400        deactivate_exception_handler();
1401        if (dev != NULL) {
1402                set_disk_named(device_name, NULL);
1403                set_disk_named(device_name, ped_disk_new(dev));
1404        }
1405        activate_exception_handler();
1406        unchange_named(device_name);
1407        oprintf("OK\n");
1408}
1409
1410void
1411command_partitions()
1412{
1413        PedPartition *part;
1414        PedConstraint *creation_constraint;
1415        PedSector grain_size;
1416        scan_device_name();
1417        if (dev == NULL)
1418                critical_error("The device %s is not opened.", device_name);
1419        log("command_partitions()");
1420        open_out();
1421        oprintf("OK\n");
1422        deactivate_exception_handler();
1423        if (disk == NULL) {
1424                log("No partitions");
1425                /* No label, hence no partitions.  When there is a
1426                   label, there is at least one partition because the
1427                   free space counts as a partition. */
1428                oprintf("\n");
1429                activate_exception_handler();
1430                return;
1431        }
1432        if (has_extended_partition(disk))
1433                minimize_extended_partition(disk);
1434        creation_constraint = partition_creation_constraint(dev);
1435        grain_size = creation_constraint->start_align->grain_size;
1436        ped_constraint_destroy(creation_constraint);
1437        for (part = NULL;
1438             NULL != (part = ped_disk_next_partition(disk, part));) {
1439                char *part_info;
1440                if (PED_PARTITION_EXTENDED & part->type)
1441                        continue;
1442                if (PED_PARTITION_METADATA & part->type)
1443                        continue;
1444                /* Undoubtedly the following operator is a hack.
1445                   Libparted tries to align the partitions at
1446                   appropriate boundaries but despite this it sometimes
1447                   reports free spaces due to aligning and even
1448                   allows creation of unaligned partitions in these
1449                   free spaces.  I am not sure if this is a bug or a
1450                   feature of libparted. */
1451                if (PED_PARTITION_FREESPACE & part->type
1452                    && ped_disk_type_check_feature(disk->type,
1453                                                   PED_DISK_TYPE_EXTENDED)
1454                    && ((part->geom).length
1455                        < dev->bios_geom.sectors * grain_size))
1456                        continue;
1457                /* Another hack :) */
1458                if (0 == strcmp(disk->type->name, "dvh")
1459                    && PED_PARTITION_LOGICAL & part->type)
1460                        continue;
1461                part_info = partition_info(disk, part);
1462                oprintf("%s\n", part_info);
1463                free(part_info);
1464        }
1465        log("Partitions printed");
1466        /* An empty line after the last partition */
1467        oprintf("\n");
1468        activate_exception_handler();
1469}
1470
1471void
1472command_partition_info()
1473{
1474        char *id;
1475        PedPartition *part;
1476        scan_device_name();
1477        if (dev == NULL)
1478                critical_error("The device %s is not opened.", device_name);
1479        log("command_partition_info()");
1480        open_out();
1481        if (1 != iscanf("%as", &id))
1482                critical_error("Expected partition id");
1483        log("command_partition_info: info for partition with id %s", id);
1484        part = partition_with_id(disk, id);
1485        oprintf("OK\n");
1486        deactivate_exception_handler();
1487        if (part == NULL) {
1488                log("command_partition_info: no such a partitions");
1489                oprintf("\n");
1490        } else {
1491                char *part_info;
1492                log("command_partition_info: partition found");
1493                part_info = partition_info(disk, part);
1494                oprintf("%s\n", part_info);
1495                free(part_info);
1496        }
1497        free(id);
1498        activate_exception_handler();
1499}
1500
1501void
1502command_get_chs()
1503{
1504        char *id;
1505        PedPartition *part;
1506        scan_device_name();
1507        if (dev == NULL)
1508                critical_error("The device %s is not opened.", device_name);
1509        log("command_get_chs()");
1510        open_out();
1511        if (1 != iscanf("%as", &id))
1512                critical_error("Expected partition id");
1513        log("command_get_chs: Cyl/Head/Sec for partition with id %s", id);
1514        part = partition_with_id(disk, id);
1515        oprintf("OK\n");
1516        deactivate_exception_handler();
1517        if (part == NULL) {
1518                log("command_get_chs: no such a partitions");
1519                oprintf("\n");
1520        } else {
1521                /* TODO: there is the same code in dump_info */
1522                long long cylinder_size, track_size;
1523                long long start, end;
1524                long long cyl_start, cyl_end;
1525                long long head_start, head_end;
1526                long long sec_start, sec_end;
1527                log("command_get_chs: partition found");
1528                track_size = dev->bios_geom.sectors;
1529                cylinder_size = track_size * dev->bios_geom.heads;
1530                start = (part->geom).start;
1531                end = (part->geom).end;
1532                cyl_start = start / cylinder_size;
1533                cyl_end = end / cylinder_size;
1534                start = start % cylinder_size;
1535                end = end % cylinder_size;
1536                head_start = start / track_size;
1537                head_end = end / track_size;
1538                sec_start = start % track_size;
1539                sec_end = end % track_size;
1540                oprintf("%lli\t%lli\t%lli\t%lli\t%lli\t%lli\n",
1541                        cyl_start, head_start, sec_start,
1542                        cyl_end, head_end, sec_end);
1543        }
1544        free(id);
1545        activate_exception_handler();
1546}
1547
1548void
1549command_label_types()
1550{
1551        PedDiskType *type = NULL;
1552        scan_device_name();
1553        if (dev == NULL)
1554                critical_error("The device %s is not opened.", device_name);
1555        log("command_label_types()");
1556        open_out();
1557        oprintf("OK\n");
1558        deactivate_exception_handler();
1559        while (NULL != (type = ped_disk_type_get_next(type))) {
1560                oprintf("%s\n", type->name);
1561        }
1562        oprintf("\n");
1563        activate_exception_handler();
1564}
1565
1566void
1567command_valid_flags()
1568{
1569        char *id;
1570        PedPartition *part;
1571        PedPartitionFlag flag;
1572        scan_device_name();
1573        if (dev == NULL)
1574                critical_error("The device %s is not opened.", device_name);
1575        log("command_valid_flags()");
1576        open_out();
1577        if (1 != iscanf("%as", &id))
1578                critical_error("Expected partition id");
1579        part = partition_with_id(disk, id);
1580        oprintf("OK\n");
1581        deactivate_exception_handler();
1582        if (part == NULL || !ped_partition_is_active(part)) {
1583                log("No such active partition: %s", id);
1584        } else {
1585                log("Partition found (%s)", id);
1586                for (flag = 0; 0 != (flag = ped_partition_flag_next(flag));)
1587                        if (ped_partition_is_flag_available(part, flag))
1588                                oprintf("%s\n",
1589                                        ped_partition_flag_get_name(flag));
1590        }
1591        oprintf("\n");
1592        free(id);
1593        activate_exception_handler();
1594}
1595
1596void
1597command_get_flags()
1598{
1599        char *id;
1600        PedPartition *part;
1601        PedPartitionFlag flag;
1602        scan_device_name();
1603        if (dev == NULL)
1604                critical_error("The device %s is not opened.", device_name);
1605        log("command_get_flags()");
1606        open_out();
1607        if (1 != iscanf("%as", &id))
1608                critical_error("Expected partition id");
1609        part = partition_with_id(disk, id);
1610        if (part == NULL || !ped_partition_is_active(part))
1611                critical_error("No such active partition: %s", id);
1612        log("Partition found (%s)", id);
1613        oprintf("OK\n");
1614        deactivate_exception_handler();
1615        for (flag = 0; 0 != (flag = ped_partition_flag_next(flag));)
1616                if (ped_partition_is_flag_available(part, flag)
1617                    && ped_partition_get_flag(part, flag))
1618                        oprintf("%s\n", ped_partition_flag_get_name(flag));
1619        oprintf("\n");
1620        free(id);
1621        activate_exception_handler();
1622}
1623
1624void
1625command_set_flags()
1626{
1627        char *id, *str;
1628        PedPartition *part;
1629        PedPartitionFlag first, last, flag;
1630        bool *states;
1631        scan_device_name();
1632        if (dev == NULL)
1633                critical_error("The device %s is not opened.", device_name);
1634        log("command_set_flags()");
1635        change_named(device_name);
1636        open_out();
1637        if (1 != iscanf("%as", &id))
1638                critical_error("Expected partition id");
1639        part = partition_with_id(disk, id);
1640        if (part == NULL || !ped_partition_is_active(part))
1641                critical_error("No such active partition: %s", id);
1642        log("Partition found (%s)", id);
1643        oprintf("OK\n");
1644        deactivate_exception_handler();
1645        first = ped_partition_flag_next(0);
1646        last = first - 1;
1647        for (flag = first; flag != 0; flag = ped_partition_flag_next(flag))
1648                last = flag;
1649        states = malloc(sizeof(bool[last - first + 1]));
1650        for (flag = first; flag <= last; flag++)
1651                states[flag - first] = false;
1652        while (1) {
1653                iscan_line(&str, 1);
1654                if (!str)
1655                        critical_error("No data in infifo!");
1656                if (!strcmp(str, "NO_MORE"))
1657                        break;
1658                log("Processing flag %s", str);
1659                flag = ped_partition_flag_get_by_name(str);
1660                if (flag >= first && flag <= last) {
1661                        log("The flag set true.");
1662                        states[flag - first] = true;
1663                }
1664                free(str);
1665        }
1666        free(str);
1667        for (flag = 0; 0 != (flag = ped_partition_flag_next(flag));)
1668                if (ped_partition_is_flag_available(part, flag))
1669                        ped_partition_set_flag(part, flag,
1670                                               states[flag - first]);
1671        free(states);
1672        activate_exception_handler();
1673        free(id);
1674}
1675
1676void
1677command_uses_names()
1678{
1679        scan_device_name();
1680        if (dev == NULL)
1681                critical_error("The device %s is not opened.", device_name);
1682        log("command_uses_names()");
1683        open_out();
1684        oprintf("OK\n");
1685        deactivate_exception_handler();
1686        if (ped_disk_type_check_feature(disk->type,
1687                                        PED_DISK_TYPE_PARTITION_NAME))
1688                oprintf("yes\n");
1689        else
1690                oprintf("no\n");
1691        activate_exception_handler();
1692}
1693
1694void
1695command_set_name()
1696{
1697        char *id, *name;
1698        PedPartition *part;
1699        scan_device_name();
1700        if (dev == NULL)
1701                critical_error("The device %s is not opened.", device_name);
1702        log("command_set_name()");
1703        change_named(device_name);
1704        if (!ped_disk_type_check_feature(disk->type,
1705                                         PED_DISK_TYPE_PARTITION_NAME))
1706                critical_error("This label doesn't support partition names.");
1707        if (1 != iscanf("%as", &id))
1708                critical_error("Expected partition id");
1709        part = partition_with_id(disk, id);
1710        if (part == NULL || !ped_partition_is_active(part))
1711                critical_error("No such active partition: %s", id);
1712        log("Partition found (%s)", id);
1713        iscan_line(&name, 0);
1714        if (!name)
1715                critical_error("No data in infifo!");
1716        log("Changing name to %s", name);
1717        open_out();
1718        oprintf("OK\n");
1719        deactivate_exception_handler();
1720        ped_partition_set_name(part, name);
1721        free(name);
1722        free(id);
1723        activate_exception_handler();
1724}
1725
1726void
1727command_get_max_primary()
1728{
1729        scan_device_name();
1730        if (dev == NULL)
1731                critical_error("The device %s is not opened.", device_name);
1732        log("command_get_max_primary()");
1733        open_out();
1734        oprintf("OK\n");
1735        deactivate_exception_handler();
1736        if (disk != NULL && disk->type != NULL)
1737                oprintf("%d\n",
1738                        ped_disk_get_max_primary_partition_count(disk));
1739        else
1740                oprintf("\n");
1741        activate_exception_handler();
1742}
1743
1744void
1745command_uses_extended()
1746{
1747        scan_device_name();
1748        if (dev == NULL)
1749                critical_error("The device %s is not opened.", device_name);
1750        log("command_uses_extended()");
1751        open_out();
1752        oprintf("OK\n");
1753        deactivate_exception_handler();
1754        if (disk != NULL && disk->type != NULL
1755            && ped_disk_type_check_feature(disk->type, PED_DISK_TYPE_EXTENDED)
1756            && 0 != strcmp(disk->type->name, "dvh"))
1757                oprintf("yes\n");
1758        else
1759                oprintf("no\n");
1760        activate_exception_handler();
1761}
1762
1763void
1764command_file_system_types()
1765{
1766        PedFileSystemType *type;
1767        scan_device_name();
1768        if (dev == NULL)
1769                critical_error("The device %s is not opened.", device_name);
1770        log("command_file_system_types()");
1771        open_out();
1772        oprintf("OK\n");
1773        deactivate_exception_handler();
1774        for (type = NULL;
1775             NULL != (type = ped_file_system_type_get_next(type));)
1776                oprintf("%s\n", type->name);
1777        oprintf("\n");
1778        activate_exception_handler();
1779}
1780
1781void
1782command_get_file_system()
1783{
1784        char *id;
1785        PedPartition *part;
1786        PedFileSystemType *fstype;
1787        scan_device_name();
1788        if (dev == NULL)
1789                critical_error("The device %s is not opened.", device_name);
1790        log("command_get_file_system()");
1791        open_out();
1792        if (1 != iscanf("%as", &id))
1793                critical_error("Expected partition id");
1794        log("command_get_file_system: File system for partition %s", id);
1795        part = partition_with_id(disk, id);
1796        oprintf("OK\n");
1797        if (named_partition_is_virtual(device_name,
1798                                       part->geom.start, part->geom.end)) {
1799                oprintf("none\n");
1800        } else {
1801                deactivate_exception_handler();
1802                fstype = ped_file_system_probe(&(part->geom));
1803                if (fstype == NULL) {
1804                        oprintf("none\n");
1805                } else {
1806                        if (0 == strncmp(fstype->name, "linux-swap", 10))
1807                                oprintf("linux-swap\n");
1808                        else
1809                                oprintf("%s\n", fstype->name);
1810                }
1811                free(id);
1812                activate_exception_handler();
1813        }
1814}
1815
1816void
1817command_change_file_system()
1818{
1819        char *id;
1820        PedPartition *part;
1821        char *s_fstype;
1822        PedFileSystemType *fstype;
1823        PedPartitionFlag flag;
1824        scan_device_name();
1825        if (dev == NULL)
1826                critical_error("The device %s is not opened.", device_name);
1827        open_out();
1828        if (2 != iscanf("%as %as", &id, &s_fstype))
1829                critical_error("Expected partition id and file system");
1830        log("command_change_file_system(%s,%s)", id, s_fstype);
1831        part = partition_with_id(disk, id);
1832        if (part == NULL) {
1833                critical_error("Partition not found: %s", id);
1834        }
1835        free(id);
1836
1837        mangle_fstype_name(&s_fstype);
1838
1839        fstype = ped_file_system_type_get(s_fstype);
1840        if (fstype == NULL) {
1841                log("Filesystem %s not found, let's see if it is a flag",
1842                    s_fstype);
1843                flag = ped_partition_flag_get_by_name(s_fstype);
1844                if (ped_partition_is_flag_available(part, flag)) {
1845                        if (!ped_partition_get_flag(part, flag)) {
1846                                change_named(device_name);
1847                                ped_partition_set_flag(part, flag, 1);
1848                        } else
1849                                log("Flag %s already set", s_fstype);
1850                } else {
1851                        critical_error("Bad file system or flag type: %s",
1852                                       s_fstype);
1853                }
1854        } else {
1855                if (!((PED_PARTITION_FREESPACE | PED_PARTITION_METADATA |
1856                       PED_PARTITION_EXTENDED) & part->type) &&
1857                    fstype != part->fs_type) {
1858                        change_named(device_name);
1859                        ped_partition_set_system(part, fstype);
1860                } else
1861                        log("Already using filesystem %s", s_fstype);
1862        }
1863        free(s_fstype);
1864        oprintf("OK\n");
1865}
1866
1867void
1868command_new_label()
1869{
1870        PedDiskType *type;
1871        char *str, *device;
1872        scan_device_name();
1873        if (dev == NULL)
1874                critical_error("The device %s is not opened.", device_name);
1875        log("command_new_label()");
1876        change_named(device_name);
1877        open_out();
1878        if (1 != iscanf("%as", &str))
1879                critical_error("Expected label type");
1880        type = ped_disk_type_get(str);
1881        if (type == NULL)
1882                critical_error("Bad label type: %s", str);
1883        log("command_new_label: requested label with type %s", str);
1884        device = strdup(device_named(device_name)->path);
1885        /* The old partition table may have contained wrong
1886           Cylinder/Head/Sector geometry.  So it is not probably
1887           enough to change the partition table (i.e. `disk'). */
1888        set_disk_named(device_name, NULL);
1889        set_device_named(device_name, NULL);
1890        dev = ped_device_get(device);
1891        free(device);
1892        if (NULL == dev)
1893                critical_error("Cannot reopen %s", device_name);
1894        set_device_named(device_name, dev);
1895        log("command_new_label: creating");
1896        disk = ped_disk_new_fresh(dev, type);
1897        if (disk == NULL) {
1898                static char *only_cancel[] = { "Cancel", NULL };
1899                pseudo_exception("Error",
1900                                 "Can't create new disk label.", only_cancel);
1901        } else
1902                set_disk_named(device_name, disk);
1903        oprintf("OK\n");
1904        free(str);
1905}
1906
1907void
1908command_new_partition()
1909{
1910        char *s_type, *s_fs_type;
1911        PedPartitionType type;
1912        PedFileSystemType *fs_type;
1913        long long range_start, range_end;
1914        char *position;
1915        PedSector length;
1916        PedSector part_start, part_end;
1917        PedPartition *part;
1918        int n;
1919        scan_device_name();
1920        if (dev == NULL)
1921                critical_error("The device %s is not opened.", device_name);
1922        assert(disk != NULL);
1923        log("command_new_partition()");
1924        change_named(device_name);
1925        open_out();
1926        n = iscanf("%as %as %lli-%lli %as %lli", &s_type, &s_fs_type,
1927                   &range_start, &range_end, &position, &length);
1928        if (n != 6)
1929                critical_error
1930                    ("Expected: part_type file_system id position length");
1931
1932        if (!strcasecmp(s_type, "primary"))
1933                type = 0;
1934        else if (!strcasecmp(s_type, "logical"))
1935                type = PED_PARTITION_LOGICAL;
1936        else
1937                critical_error("Bad partition type: %s", s_type);
1938        log("requested partition with type %s", s_type);
1939        free(s_type);
1940
1941        mangle_fstype_name(&s_fs_type);
1942
1943        fs_type = ped_file_system_type_get(s_fs_type);
1944        if (fs_type == NULL)
1945                critical_error("Bad file system type: %s", s_fs_type);
1946        log("requested partition with file system %s", s_fs_type);
1947        free(s_fs_type);
1948
1949        if (!strcasecmp(position, "full")) {
1950                part_start = range_start / dev->sector_size;
1951                part_end = ((range_end - dev->sector_size + 1)
1952                            / dev->sector_size);
1953        } else if (!strcasecmp(position, "beginning")) {
1954                part_start = range_start / dev->sector_size;
1955                part_end = (range_start + length) / dev->sector_size;
1956        } else if (!strcasecmp(position, "end")) {
1957                part_start = (range_end - length) / dev->sector_size;
1958                part_end = ((range_end - dev->sector_size + 1)
1959                            / dev->sector_size);
1960        } else
1961                critical_error("Bad position: %s", position);
1962        free(position);
1963
1964        if (disk == NULL)
1965                critical_error("No opened device or no partition table");
1966
1967        if (type == 0 /* PED_PARTITION_PRIMARY */ )
1968                part = add_primary_partition(disk, fs_type,
1969                                             part_start, part_end);
1970        else
1971                part = add_logical_partition(disk, fs_type,
1972                                             part_start, part_end);
1973        oprintf("OK\n");
1974        deactivate_exception_handler();
1975        if (part) {
1976                char *part_info = partition_info(disk, part);
1977                oprintf("%s\n", part_info);
1978                free(part_info);
1979        } else
1980                oprintf("\n");
1981        activate_exception_handler();
1982}
1983
1984void
1985command_delete_partition()
1986{
1987        PedPartition *part;
1988        char *id;
1989        scan_device_name();
1990        if (dev == NULL)
1991                critical_error("The device %s is not opened.", device_name);
1992        log("command_delete_partition()");
1993        change_named(device_name);
1994        open_out();
1995        if (1 != iscanf("%as", &id))
1996                critical_error("Expected partition id");
1997        log("Deleting partition with id %s", id);
1998        part = partition_with_id(disk, id);
1999        if (part == NULL)
2000                log("No such partition");
2001        else {
2002                PedPartitionType type = part->type;
2003                log("Partition found");
2004                ped_disk_delete_partition(disk, part);
2005                if (type & PED_PARTITION_LOGICAL)
2006                        minimize_extended_partition(disk);
2007                log("Partition deleted");
2008        }
2009        oprintf("OK\n");
2010        free(id);
2011}
2012
2013void
2014command_resize_partition()
2015{
2016        PedPartition *part;
2017        char *id;
2018        long long new_size;
2019        PedSector start, end;
2020        scan_device_name();
2021        if (dev == NULL)
2022                critical_error("The device %s is not opened.", device_name);
2023        assert(disk != NULL);
2024        log("command_resize_partition()");
2025        change_named(device_name);
2026        open_out();
2027        if (1 != iscanf("%as", &id))
2028                critical_error("Expected partition id");
2029        log("Resizing partition with id %s", id);
2030        part = partition_with_id(disk, id);
2031        if (part == NULL)
2032                critical_error("No such partition");
2033        if (1 != iscanf(" %lli", &new_size))
2034                critical_error("Expected new size");
2035        log("New size: %lli", new_size);
2036        start = (part->geom).start;
2037        end = start + new_size / dev->sector_size - 1;
2038        if (named_partition_is_virtual(device_name,
2039                                       part->geom.start, part->geom.end)) {
2040                resize_partition(disk, part, start, end, false);
2041        } else {
2042                if (resize_partition(disk, part, start, end, true)) {
2043                        ped_disk_commit(disk);
2044                        unchange_named(device_name);
2045                }
2046        }
2047        oprintf("OK\n");
2048        oprintf("%lli-%lli\n", (part->geom).start * dev->sector_size,
2049                (part->geom).end * dev->sector_size + dev->sector_size - 1);
2050        free(id);
2051}
2052
2053void
2054command_virtual_resize_partition()
2055{
2056        PedPartition *part;
2057        char *id;
2058        long long new_size;
2059        PedSector start, end;
2060        scan_device_name();
2061        if (dev == NULL)
2062                critical_error("The device %s is not opened.", device_name);
2063        assert(disk != NULL);
2064        log("command_virtual_resize_partition()");
2065        change_named(device_name);
2066        open_out();
2067        if (1 != iscanf("%as", &id))
2068                critical_error("Expected partition id");
2069        log("Resizing partition with id %s", id);
2070        part = partition_with_id(disk, id);
2071        if (part == NULL)
2072                critical_error("No such partition");
2073        if (1 != iscanf(" %lli", &new_size))
2074                critical_error("Expected new size");
2075        log("New size: %lli", new_size);
2076        start = (part->geom).start;
2077        end = start + new_size / dev->sector_size - 1;
2078        /* ensure that the size is not less than the requested */
2079        do {
2080                resize_partition(disk, part, start, end, false);
2081                end = end + 1;
2082        } while ((part->geom).length * dev->sector_size < new_size);
2083        ped_disk_commit(disk);
2084        unchange_named(device_name);
2085        oprintf("OK\n");
2086        oprintf("%lli-%lli\n", (part->geom).start * dev->sector_size,
2087                (part->geom).end * dev->sector_size + dev->sector_size - 1);
2088        free(id);
2089}
2090
2091void
2092command_get_resize_range()
2093{
2094        char *id;
2095        PedPartition *part;
2096        PedFileSystem *fs;
2097        PedConstraint *constraint;
2098        PedGeometry *max_geom;
2099        long long max_size, min_size, current_size;
2100        scan_device_name();
2101        if (dev == NULL)
2102                critical_error("The device %s is not opened.", device_name);
2103        assert(disk != NULL);
2104        log("command_get_resize_range()");
2105        open_out();
2106        if (1 != iscanf("%as", &id))
2107                critical_error("Expected partition id");
2108        deactivate_exception_handler();
2109        part = partition_with_id(disk, id);
2110        if (part == NULL)
2111                critical_error("No such partition");
2112        if (!named_partition_is_virtual(device_name,
2113                                        part->geom.start, part->geom.end)) {
2114                fs = ped_file_system_open(&(part->geom));
2115                if (NULL != fs && (fs->geom->start < (part->geom).start
2116                                   || fs->geom->end > (part->geom).end)) {
2117                        ped_file_system_close(fs);
2118                        fs = NULL;
2119                } else if (NULL == fs
2120                           && NULL != ped_file_system_probe(&(part->geom))) {
2121                        oprintf("OK\n");
2122                        oprintf("\n");
2123                        free(id);
2124                        activate_exception_handler();
2125                        return;
2126                }
2127        } else {
2128                fs = NULL;
2129        }
2130        if (NULL != fs) {
2131                constraint = ped_file_system_get_resize_constraint(fs);
2132                ped_file_system_close(fs);
2133        } else {
2134                constraint = ped_constraint_any(disk->dev);
2135        }
2136        ped_geometry_set_start(constraint->start_range, (part->geom).start);
2137        ped_geometry_set_end(constraint->start_range, (part->geom).start);
2138        if (part->type & PED_PARTITION_LOGICAL)
2139                maximize_extended_partition(disk);
2140        max_geom = ped_disk_get_max_partition_geometry(disk, part, constraint);
2141        if (part->type & PED_PARTITION_LOGICAL)
2142                minimize_extended_partition(disk);
2143        min_size = constraint->min_size * dev->sector_size;
2144        current_size = (part->geom).length * dev->sector_size;
2145        if (max_geom)
2146                max_size = max_geom->length * dev->sector_size;
2147        else
2148                max_size = current_size;
2149        oprintf("OK\n");
2150        oprintf("%lli %lli %lli\n", min_size, current_size, max_size);
2151        if (max_geom)
2152                ped_geometry_destroy(max_geom);
2153        ped_constraint_destroy(constraint);
2154        /* TODO: Probably there are memory leaks because of constraints. */
2155        activate_exception_handler();
2156        free(id);
2157}
2158
2159void
2160command_get_virtual_resize_range()
2161{
2162        char *id;
2163        PedPartition *part;
2164        PedConstraint *constraint;
2165        PedGeometry *max_geom;
2166        long long max_size, min_size, current_size;
2167        scan_device_name();
2168        if (dev == NULL)
2169                critical_error("The device %s is not opened.", device_name);
2170        assert(disk != NULL);
2171        log("command_get_virtual_resize_range()");
2172        open_out();
2173        if (1 != iscanf("%as", &id))
2174                critical_error("Expected partition id");
2175        deactivate_exception_handler();
2176        part = partition_with_id(disk, id);
2177        if (part == NULL)
2178                critical_error("No such partition");
2179        constraint = ped_constraint_any(disk->dev);
2180        ped_geometry_set_start(constraint->start_range, (part->geom).start);
2181        ped_geometry_set_end(constraint->start_range, (part->geom).start);
2182        if (part->type & PED_PARTITION_LOGICAL)
2183                maximize_extended_partition(disk);
2184        max_geom = ped_disk_get_max_partition_geometry(disk, part, constraint);
2185        if (part->type & PED_PARTITION_LOGICAL)
2186                minimize_extended_partition(disk);
2187        min_size = constraint->min_size * dev->sector_size;
2188        current_size = (part->geom).length * dev->sector_size;
2189        if (max_geom)
2190                max_size = max_geom->length * dev->sector_size;
2191        else
2192                max_size = current_size;
2193        oprintf("OK\n");
2194        oprintf("%lli %lli %lli\n", min_size, current_size, max_size);
2195        if (max_geom)
2196                ped_geometry_destroy(max_geom);
2197        ped_constraint_destroy(constraint);
2198        /* TODO: Probably there are memory leaks because of constraints. */
2199        activate_exception_handler();
2200        free(id);
2201}
2202
2203void
2204command_get_label_type()
2205{
2206        log("command_get_label_type()");
2207        scan_device_name();
2208        open_out();
2209        oprintf("OK\n");
2210        deactivate_exception_handler();
2211        if ((disk == NULL) || (disk->type == NULL)
2212            || (disk->type->name == NULL)) {
2213                oprintf("unknown\n");
2214        } else {
2215                oprintf("%s\n", disk->type->name);
2216        }
2217        activate_exception_handler();
2218}
2219
2220void
2221command_is_busy()
2222{
2223        char *id;
2224        PedPartition *part;
2225        log("command_is_busy()");
2226        scan_device_name();
2227        if (dev == NULL)
2228                critical_error("The device %s is not opened.", device_name);
2229        open_out();
2230        if (1 != iscanf("%as", &id))
2231                critical_error("Expected partition id");
2232        log("command_is_busy: busy check for id %s", id);
2233        part = partition_with_id(disk, id);
2234        oprintf("OK\n");
2235        if (ped_partition_is_busy(part)) {
2236                oprintf("yes\n");
2237        } else {
2238                oprintf("no\n");
2239        }
2240        free(id);
2241}
2242
2243void
2244command_alignment_offset()
2245{
2246        char *id;
2247        PedPartition *part;
2248        PedAlignment *align;
2249        log("command_alignment_offset()");
2250        scan_device_name();
2251        if (dev == NULL)
2252                critical_error("The device %s is not opened.", device_name);
2253        open_out();
2254        if (1 != iscanf("%as", &id))
2255                critical_error("Expected partition id");
2256        part = partition_with_id(disk, id);
2257        oprintf("OK\n");
2258        if (alignment_of_device(dev) == ALIGNMENT_CYLINDER)
2259                /* None of this is useful when using cylinder alignment. */
2260                oprintf("0\n");
2261        else {
2262                align = ped_device_get_minimum_alignment(dev);
2263
2264                /* align->offset represents the offset of the lowest logical
2265                 * block on the disk from the disk's natural alignment,
2266                 * modulo the physical sector size (e.g. 4096 bytes), as a
2267                 * number of logical sectors (e.g. 512 bytes).  For a disk
2268                 * with 4096-byte physical sectors deliberately misaligned
2269                 * to make DOS-style 63-sector offsets work well, we would
2270                 * thus expect align->offset to be 1, as (1 + 63) * 512 /
2271                 * 4096 is an integer.
2272                 *
2273                 * To get the alignment offset of a *partition*, we thus
2274                 * need to start with align->offset (in bytes) plus the
2275                 * partition start position.
2276                 */
2277                oprintf("%lld\n",
2278                        ((align->offset + part->geom.start) *
2279                         dev->sector_size) %
2280                        dev->phys_sector_size);
2281
2282                ped_alignment_destroy(align);
2283        }
2284        free(id);
2285}
2286
2287void
2288make_fifo(char* name)
2289{
2290    int status;
2291    status = mkfifo(name, 0644);
2292    if ((status != 0))
2293            if (errno != EEXIST) {
2294                    perror("Cannot create FIFO");
2295                    exit(252);
2296            }
2297}
2298
2299void
2300make_fifos()
2301{
2302    make_fifo(infifo_name);
2303    make_fifo(outfifo_name);
2304    make_fifo(stopfifo_name);
2305} 
2306
2307int
2308write_pid_file()
2309{
2310        FILE *fd;
2311        int status;
2312        pid_t oldpid;
2313        if ((fd = fopen(pidfile_name, "a+")) == NULL)
2314                return -1;
2315
2316        status = fscanf(fd, "%d", &oldpid);
2317        if (status != 0 && status != EOF) {
2318                // If kill(oldpid, 0) == 0 the process is still alive
2319                // so we abort
2320                if (kill(oldpid, 0) == 0) {
2321                        fprintf(stderr, "Not starting: process %d still exists\n", oldpid);
2322                        fclose(fd);
2323                        exit(250);
2324                }
2325        }
2326
2327        // Truncate the pid file and continue
2328        freopen(pidfile_name, "w", fd);
2329     
2330        fprintf(fd, "%d", (int)(getpid()));
2331        fclose(fd);
2332        return 0;
2333}
2334
2335void
2336cleanup_and_die()
2337{
2338        if (unlink(pidfile_name) != 0)
2339                perror("Cannot unlink pid file");
2340        if (unlink(infifo_name) != 0)
2341                perror("Cannot unlink input FIFO");
2342        if (unlink(outfifo_name) != 0)
2343                perror("Cannot unlink output FIFO");
2344        if (unlink(stopfifo_name) != 0)
2345                perror("Cannot unlink stop FIFO");
2346}
2347
2348void
2349prnt_sig_hdlr(int signal)
2350{
2351        int status;
2352        switch(signal) {
2353                // SIGUSR1 signals that child is ready to take
2354                // requests (i.e. has finished initialisation)
2355                case SIGUSR1:
2356                    exit(0);
2357                    break;
2358                // We'll only get SIGCHLD if our child has pre-deceased us
2359                // In this case we should exit with its error code
2360                case SIGCHLD:
2361                    if (waitpid(-1, &status, WNOHANG) < 0)
2362                        exit(0);
2363                    if (WIFEXITED(status))
2364                        exit(WEXITSTATUS(status));
2365                    break;
2366                default:
2367                    break;
2368        }
2369}
2370
2371/**********************************************************************
2372   Main
2373**********************************************************************/
2374
2375void main_loop() __attribute__ ((noreturn));
2376
2377void
2378main_loop()
2379{
2380        char *str;
2381        int iteration = 1;
2382        set_alignment();
2383        while (1) {
2384                log("main_loop: iteration %i", iteration++);
2385                open_in();
2386                if (1 != iscanf("%as", &str))
2387                        critical_error("No data in infifo.");
2388                log("Read command: %s", str);
2389                /* Keep partman-command in sync with changes here. */
2390                if (!strcasecmp(str, "QUIT"))
2391                        command_quit();
2392                else if (!strcasecmp(str, "OPEN"))
2393                        command_open();
2394                else if (!strcasecmp(str, "CLOSE"))
2395                        command_close();
2396                else if (!strcasecmp(str, "OPENED"))
2397                        command_opened();
2398                else if (!strcasecmp(str, "VIRTUAL"))
2399                        command_virtual();
2400                else if (!strcasecmp(str, "DISK_UNCHANGED"))
2401                        command_disk_unchanged();
2402                else if (!strcasecmp(str, "IS_CHANGED"))
2403                        command_is_changed();
2404                else if (!strcasecmp(str, "DUMP"))
2405                        command_dump();
2406                else if (!strcasecmp(str, "COMMIT"))
2407                        command_commit();
2408                else if (!strcasecmp(str, "UNDO"))
2409                        command_undo();
2410                else if (!strcasecmp(str, "PARTITIONS"))
2411                        command_partitions();
2412                else if (!strcasecmp(str, "PARTITION_INFO"))
2413                        command_partition_info();
2414                else if (!strcasecmp(str, "GET_CHS"))
2415                        command_get_chs();
2416                else if (!strcasecmp(str, "LABEL_TYPES"))
2417                        command_label_types();
2418                else if (!strcasecmp(str, "VALID_FLAGS"))
2419                        command_valid_flags();
2420                else if (!strcasecmp(str, "GET_FLAGS"))
2421                        command_get_flags();
2422                else if (!strcasecmp(str, "SET_FLAGS"))
2423                        command_set_flags();
2424                else if (!strcasecmp(str, "SET_NAME"))
2425                        command_set_name();
2426                else if (!strcasecmp(str, "USES_NAMES"))
2427                        command_uses_names();
2428                else if (!strcasecmp(str, "GET_MAX_PRIMARY"))
2429                        command_get_max_primary();
2430                else if (!strcasecmp(str, "USES_EXTENDED"))
2431                        command_uses_extended();
2432                else if (!strcasecmp(str, "FILE_SYSTEM_TYPES"))
2433                        command_file_system_types();
2434                else if (!strcasecmp(str, "GET_FILE_SYSTEM"))
2435                        command_get_file_system();
2436                else if (!strcasecmp(str, "CHANGE_FILE_SYSTEM"))
2437                        command_change_file_system();
2438                else if (!strcasecmp(str, "NEW_LABEL"))
2439                        command_new_label();
2440                else if (!strcasecmp(str, "NEW_PARTITION"))
2441                        command_new_partition();
2442                else if (!strcasecmp(str, "DELETE_PARTITION"))
2443                        command_delete_partition();
2444                else if (!strcasecmp(str, "RESIZE_PARTITION"))
2445                        command_resize_partition();
2446                else if (!strcasecmp(str, "GET_RESIZE_RANGE"))
2447                        command_get_resize_range();
2448                /* these two functions are undocumented and should disappear */
2449                else if (!strcasecmp(str, "VIRTUAL_RESIZE_PARTITION"))
2450                        command_virtual_resize_partition();
2451                else if (!strcasecmp(str, "GET_VIRTUAL_RESIZE_RANGE"))
2452                        command_get_virtual_resize_range();
2453                else if (!strcasecmp(str, "GET_LABEL_TYPE"))
2454                        command_get_label_type();
2455                else if (!strcasecmp(str, "IS_BUSY"))
2456                        command_is_busy();
2457                else if (!strcasecmp(str, "ALIGNMENT_OFFSET"))
2458                        command_alignment_offset();
2459                else
2460                        critical_error("Unknown command %s", str);
2461                free(str);
2462                close_fifos_and_synchronise();
2463        }
2464}
2465
2466int
2467main(int argc, char *argv[])
2468{
2469        struct sigaction act, oldact;
2470        int i;
2471
2472        /* Close all extraneous file descriptors, including our pipe to
2473         * debconf.
2474         */
2475        for (i = 3; i < 256; ++i)
2476                close(i);
2477
2478        // Set up signal handling
2479        memset(&act,0,sizeof(struct sigaction));
2480        memset(&oldact,0,sizeof(struct sigaction));
2481        act.sa_handler = prnt_sig_hdlr;
2482        sigemptyset(&act.sa_mask);
2483
2484        // Set up signal handling for parent
2485        if  ((sigaction(SIGCHLD, &act, &oldact) < 0)
2486          || (sigaction(SIGUSR1, &act, &oldact) < 0))
2487        {
2488            fprintf(stderr, "Could not set up signal handling for parent\n");
2489            exit(251);
2490        }
2491     
2492        // The parent process should wait; we die once child is
2493        // initialised (signalled by a SIGUSR1)
2494        if (fork()) {
2495            while (1) { sleep(5); };
2496        }
2497
2498        // Set up signal handling for child
2499        if  ((sigaction(SIGCHLD, &oldact, NULL) < 0)
2500          || (sigaction(SIGUSR1, &oldact, NULL) < 0))
2501        {
2502            fprintf(stderr, "Could not set up signal handling for child\n");
2503            exit(250);
2504        }
2505
2506        // Continue as a daemon process
2507        logfile = fopen(logfile_name, "a+");
2508        if (logfile == NULL) {
2509                fprintf(stderr, "Cannot append to the log file\n");
2510                exit(255);
2511        }
2512        if (write_pid_file() != 0) {
2513                fprintf(stderr, "Cannot open pid file\n");
2514                exit(254);
2515        }
2516        if (atexit(cleanup_and_die) != 0) {
2517                fprintf(stderr, "Cannot set atexit routine\n");
2518                exit(253);
2519        }
2520        make_fifos();
2521        // Signal that we've finished initialising so that the parent process
2522        // can die and the shell scripts can continue
2523        kill(getppid(), SIGUSR1);
2524        ped_exception_set_handler(exception_handler);
2525        log("======= Starting the server");
2526        main_loop();
2527}
2528
2529/*
2530The following command can be used to format this file in a consistent
2531with codingstyle.txt way:
2532
2533indent parted_server.c -kr -i8 -nut -psl -l79 -T FILE -T bool -T PedSector -T PedDeviceType -T PedDevice -T PedDiskTypeFeature -T PedDiskType -T PedDisk -T PedGeometry -T PedPartitionType -T PedPartitionFlag -T PedPartition -T PedFileSystemType -T PedFileSystem -T PedConstraint -T PedAlignment -T PedTimer -T PedExceptionType -T PedExceptionOption -T PedException
2534*/
2535
2536/*
2537Local variables:
2538indent-tabs-mode: nil
2539c-file-style: "linux"
2540c-font-lock-extra-types: ("FILE" "\\sw+_t" "bool" "Ped\\sw+")
2541End:
2542*/
Note: See TracBrowser for help on using the repository browser.