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

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

add ubiquity

File size: 88.4 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                  && possible_primary_partition(disk, space));
961        activate_exception_handler();
962        return result;
963}
964
965/* true if the last sector of `part1' is phisicaly before the first
966   sector of `part2'. */
967inline bool
968partition_before(PedPartition *part1, PedPartition *part2)
969{
970        return (part1->geom).end < (part2->geom).start;
971}
972
973/* true when it is possible to create a logical partition in `space'.
974   `space' must be a free space in `disk'. */
975bool
976possible_logical_partition(PedDisk *disk, PedPartition *space)
977{
978        PedPartition *extended, *part;
979        bool result;
980        assert(disk != NULL);
981        assert(space != NULL && (PED_PARTITION_FREESPACE & space->type));
982        deactivate_exception_handler();
983        if (!has_extended_partition(disk))
984                result = possible_extended_partition(disk, space);
985        else {
986                extended = ped_disk_extended_partition(disk);
987                result = true;
988                part = ped_disk_next_partition(disk, NULL);
989                while (result && NULL != part) {
990                        if (ped_partition_is_active(part)
991                            && ((partition_before(space, part)
992                                 && partition_before(part, extended))
993                                || (partition_before(extended, part)
994                                    && partition_before(part, space)))) {
995                                /* There is a primary partition between us
996                                   and the extended partition. */
997                                assert(!(PED_PARTITION_LOGICAL & part->type));
998                                result = false;
999                        }
1000                        part = ped_disk_next_partition(disk, part);
1001                }
1002        }
1003        activate_exception_handler();
1004        return result;
1005}
1006
1007/* Finds in `disk' a partition with id `id' and returns it. */
1008PedPartition *
1009partition_with_id(PedDisk *disk, char *id)
1010{
1011        PedPartition *part;
1012        long long start, end;
1013        long long start_sector, end_sector;
1014        assert(id != NULL);
1015        log("partition_with_id(%s)", id);
1016        if (2 != sscanf(id, "%lli-%lli", &start, &end))
1017                critical_error("Bad id %s", id);
1018        start_sector = start / disk->dev->sector_size;
1019        end_sector = (end - disk->dev->sector_size + 1) / disk->dev->sector_size;
1020        if (disk == NULL)
1021                return NULL;
1022        for (part = NULL;
1023             NULL != (part = ped_disk_next_partition(disk, part));)
1024                if ((part->geom).start == start_sector
1025                    && (part->geom).end == end_sector)
1026                        return part;
1027        return NULL;
1028}
1029
1030/* Returns informational string about `part' from `disk'.  Format:*/
1031/* Number<TAB>id<TAB>length<TAB>type<TAB>fs<TAB>path<TAB>name */
1032char *
1033partition_info(PedDisk *disk, PedPartition *part)
1034{
1035        char const *type;
1036        char const *fs;
1037        char *path;
1038        char const *name;
1039        char *result;
1040        assert(disk != NULL && part != NULL);
1041        if (PED_PARTITION_FREESPACE & part->type) {
1042                bool possible_primary = possible_primary_partition(disk, part);
1043                bool possible_logical = possible_logical_partition(disk, part);
1044                if (possible_primary)
1045                        if (possible_logical)
1046                                type = "pri/log";
1047                        else
1048                                type = "primary";
1049                else if (possible_logical)
1050                        type = "logical";
1051                else
1052                        type = "unusable";
1053        } else if (PED_PARTITION_LOGICAL & part->type)
1054                type = "logical";
1055        else
1056                type = "primary";
1057
1058        if (PED_PARTITION_FREESPACE & part->type)
1059                fs = "free";
1060        else if (PED_PARTITION_METADATA & part->type)
1061                fs = "label";
1062        else if (PED_PARTITION_EXTENDED & part->type)
1063                fs = "extended";
1064        else if (NULL == (part->fs_type))
1065                fs = "unknown";
1066        else if (0 == strncmp(part->fs_type->name, "linux-swap", 10))
1067                fs = "linux-swap";
1068        else
1069                fs = part->fs_type->name;
1070
1071        if (0 == strcmp(disk->type->name, "loop")) {
1072                path = strdup(disk->dev->path);
1073/*         } else if (0 == strcmp(disk->type->name, "dvh")) { */
1074/*                 PedPartition *p; */
1075/*                 int count = 1; */
1076/*                 int number_offset; */
1077/*                 for (p = NULL; */
1078/*                      NULL != (p = ped_disk_next_partition(disk, p));) { */
1079/*                         if (PED_PARTITION_METADATA & p->type) */
1080/*                                 continue; */
1081/*                         if (PED_PARTITION_FREESPACE & p->type) */
1082/*                                 continue; */
1083/*                         if (PED_PARTITION_LOGICAL & p->type) */
1084/*                                 continue; */
1085/*                         if (part->num > p->num) */
1086/*                                 count++; */
1087/*                 } */
1088/*                 path = ped_partition_get_path(part); */
1089/*                 number_offset = strlen(path); */
1090/*                 while (number_offset > 0 && isdigit(path[number_offset-1])) */
1091/*                         number_offset--; */
1092/*                 sprintf(path + number_offset, "%i", count); */
1093        } else {
1094                path = ped_partition_get_path(part);
1095        }
1096        if (ped_disk_type_check_feature(part->disk->type,
1097                                        PED_DISK_TYPE_PARTITION_NAME)
1098            && ped_partition_is_active(part))
1099                name = ped_partition_get_name(part);
1100        else
1101                name = "";
1102        result = xasprintf("%i\t%lli-%lli\t%lli\t%s\t%s\t%s\t%s",
1103                           part->num,
1104                           (part->geom).start * disk->dev->sector_size,
1105                           (part->geom).end * disk->dev->sector_size + disk->dev->sector_size - 1,
1106                           (part->geom).length * disk->dev->sector_size, type, fs, path, name);
1107        free(path);
1108        return result;
1109}
1110
1111/* Print in `dumpfile' information about the `dev', `disk' and the
1112   partitions in `disk'. */
1113void
1114dump_info(FILE *dumpfile, PedDevice *dev, PedDisk *disk)
1115{
1116        PedPartition *part;
1117        deactivate_exception_handler();
1118        if (dev == NULL) {
1119                fprintf(dumpfile, "Device: no");
1120                activate_exception_handler();
1121                return;
1122        }
1123        fprintf(dumpfile, "Device: yes\n");
1124        fprintf(dumpfile, "Model: %s\n", dev->model);
1125        fprintf(dumpfile, "Path: %s\n", dev->path);
1126        fprintf(dumpfile, "Sector size: %lli\n", dev->sector_size);
1127        fprintf(dumpfile, "Sectors: %lli\n", dev->length);
1128        fprintf(dumpfile, "Sectors/track: %i\n", dev->bios_geom.sectors);
1129        fprintf(dumpfile, "Heads: %i\n", dev->bios_geom.heads);
1130        fprintf(dumpfile, "Cylinders: %i\n", dev->bios_geom.cylinders);
1131        if (disk == NULL) {
1132                fprintf(dumpfile, "Partition table: no\n");
1133                activate_exception_handler();
1134                return;
1135        }
1136        fprintf(dumpfile, "Partition table: yes\n");
1137        fprintf(dumpfile, "Type: %s\n", disk->type->name);
1138        fprintf(dumpfile, "Partitions: #\tid\tlength\ttype\tfs\tpath\tname\n");
1139        for (part = NULL;
1140             NULL != (part = ped_disk_next_partition(disk, part));) {
1141                /* TODO: there is the same code in command_get_chs */
1142                long long cylinder_size, track_size;
1143                long long start, end;
1144                long long cyl_start, cyl_end;
1145                long long head_start, head_end;
1146                long long sec_start, sec_end;
1147                char *part_info = partition_info(disk, part);
1148                track_size = dev->bios_geom.sectors;
1149                cylinder_size = track_size * dev->bios_geom.heads;
1150                start = (part->geom).start;
1151                end = (part->geom).end;
1152                cyl_start = start / cylinder_size;
1153                cyl_end = end / cylinder_size;
1154                start = start % cylinder_size;
1155                end = end % cylinder_size;
1156                head_start = start / track_size;
1157                head_end = end / track_size;
1158                sec_start = start % track_size;
1159                sec_end = end % track_size;
1160                fprintf(dumpfile,
1161                        "(%lli,%lli,%lli)\t(%lli,%lli,%lli)\t%s\n",
1162                        cyl_start, head_start, sec_start, cyl_end,
1163                        head_end, sec_end, part_info);
1164                free(part_info);
1165        }
1166        fprintf(dumpfile, "Dump finished.\n");
1167        activate_exception_handler();
1168}
1169
1170/**********************************************************************
1171   Commands
1172*********************************************************************/
1173
1174PedDevice *dev;
1175PedDisk *disk;
1176char *device_name;
1177
1178void
1179scan_device_name()
1180{
1181        if (device_name != NULL)
1182                free(device_name);
1183        if (1 != iscanf("%as", &device_name))
1184                critical_error("Expected device identifier.");
1185        dev = device_named(device_name);
1186        disk = disk_named(device_name);
1187}
1188
1189void
1190command_quit()
1191{
1192        log("Quitting");
1193        fflush(logfile);
1194        exit(0);
1195}
1196
1197void
1198command_open()
1199{
1200        log("command_open()");
1201        char *device;
1202        scan_device_name();
1203        if (1 != iscanf("%as", &device))
1204                critical_error("Expected device name.");
1205        log("Request to open %s", device_name);
1206        open_out();
1207        if (device_opened(device_name)) {
1208                static char *only_ok[] = { "OK", NULL };
1209                log("Warning: the device is already opened");
1210                pseudo_exception("Warning",
1211                                 "The device is already opened.", only_ok);
1212        } else {
1213                set_device_named(device_name, ped_device_get(device));
1214        }
1215        oprintf("OK\n");
1216        if (NULL != device_named(device_name)) {
1217                oprintf("OK\n");
1218                deactivate_exception_handler();
1219                set_disk_named(device_name,
1220                               ped_disk_new(device_named(device_name)));
1221                unchange_named(device_name);
1222                activate_exception_handler();
1223        } else
1224                oprintf("failed\n");
1225        free(device);
1226}
1227
1228void
1229command_close()
1230{
1231        log("command_close()");
1232        scan_device_name();
1233        open_out();
1234        if (!device_opened(device_name)) {
1235                static char *only_cancel[] = { "Cancel", NULL };
1236                pseudo_exception("Error",
1237                                 "The device is not opened!", only_cancel);
1238        }
1239        set_disk_named(device_name, NULL);
1240        set_device_named(device_name, NULL);
1241        oprintf("OK\n");
1242}
1243
1244void
1245command_opened()
1246{
1247        log("command_opened()");
1248        scan_device_name();
1249        open_out();
1250        oprintf("OK\n");
1251        if (NULL != device_named(device_name)) {
1252                oprintf("yes\n");
1253        } else {
1254                oprintf("no\n");
1255        }
1256}
1257
1258void
1259command_virtual()
1260{
1261        char *id;
1262        PedPartition *part;
1263        scan_device_name();
1264        if (dev == NULL)
1265                critical_error("The device %s is not opened.", device_name);
1266        log("command_virtual()");
1267        open_out();
1268        if (1 != iscanf("%as", &id))
1269                critical_error("Expected partition id");
1270        log("is virtual partition with id %s", id);
1271        part = partition_with_id(disk, id);
1272        oprintf("OK\n");
1273        if (named_partition_is_virtual(device_name,
1274                                       part->geom.start, part->geom.end)) {
1275                oprintf("yes\n");
1276        } else {
1277                oprintf("no\n");
1278        }
1279        free(id);
1280}
1281
1282void
1283command_disk_unchanged()
1284{
1285        scan_device_name();
1286        if (dev == NULL)
1287                critical_error("The device %s is not opened.", device_name);
1288        log("command_disk_unchanged(%s)", device_name);
1289        open_out();
1290        oprintf("OK\n");
1291        unchange_named(device_name);
1292}
1293
1294void
1295command_is_changed()
1296{
1297        scan_device_name();
1298        if (dev == NULL)
1299                critical_error("The device %s is not opened.", device_name);
1300        log("command_is_changed(%s)", device_name);
1301        open_out();
1302        oprintf("OK\n");
1303        if (named_is_changed(device_name))
1304                oprintf("yes\n");
1305        else
1306                oprintf("no\n");
1307}
1308
1309/* Print in /var/log/partition_dump information about the disk, the
1310   partition table and the partitions. */
1311void
1312command_dump()
1313{
1314        FILE *dumpfile;
1315        static char *only_cancel[] = { "Cancel", NULL };
1316        scan_device_name();
1317        if (dev == NULL)
1318                critical_error("The device %s is not opened.", device_name);
1319        log("command_dump()");
1320        open_out();
1321        dumpfile = fopen("/var/log/partition_dump", "a+");
1322        if (dumpfile == NULL) {
1323                pseudo_exception("Error",
1324                                 "Can't open /var/log/partition_dump",
1325                                 only_cancel);
1326        } else {
1327                dump_info(dumpfile, dev, disk);
1328                fclose(dumpfile);
1329        }
1330        oprintf("OK\n");
1331}
1332
1333/* Check whether we are running on a sunxi-based, freescale-based, or
1334   AM33XX (beaglebone black) system. */
1335int
1336is_system_with_firmware_on_disk()
1337{
1338        int cpuinfo_handle;
1339        int result = 0;
1340        char buf[4096];
1341        int length;
1342
1343        if ((cpuinfo_handle = open("/proc/cpuinfo", O_RDONLY)) != -1) {
1344                length = read(cpuinfo_handle, buf, sizeof(buf)-1);
1345                if (length > 0) {
1346                        buf[length]='\0';
1347                        if (strstr(buf, "Allwinner") != NULL)
1348                                result = 1;
1349                        else if (strstr(buf, "Freescale") != NULL)
1350                                result = 1;
1351                        else if (strstr(buf, "AM33XX") != NULL)
1352                                result = 1;
1353                }
1354                close(cpuinfo_handle);
1355        }
1356        return result;
1357}
1358
1359void
1360command_commit()
1361{
1362        scan_device_name();
1363        if (dev == NULL)
1364                critical_error("The device %s is not opened.", device_name);
1365        log("command_commit()");
1366
1367        /* The boot device on sunxi-based systems needs special handling.
1368         * By default partman calls ped_disk_clobber when writing the
1369         * partition table, but on sunxi-based systems this would overwrite
1370         * the firmware area, resulting in an unbootable system (see
1371         * bug #751704).
1372         */
1373        if (is_system_with_firmware_on_disk() && !strcmp(disk->dev->path, "/dev/mmcblk0")) {
1374                disk->needs_clobber = 0;
1375                log("Sunxi/Freescale/AM33XX detected. Disabling ped_disk_clobber" \
1376                    "for the boot device %s to protect the firmware " \
1377                    "area.", disk->dev->path);
1378        }
1379
1380        open_out();
1381        if (disk != NULL && named_is_changed(device_name))
1382                ped_disk_commit(disk);
1383        unchange_named(device_name);
1384        oprintf("OK\n");
1385}
1386
1387void
1388command_undo()
1389{
1390        scan_device_name();
1391        if (dev == NULL)
1392                critical_error("The device %s is not opened.", device_name);
1393        log("command_undo()");
1394        open_out();
1395        log("Rereading disk label");
1396        deactivate_exception_handler();
1397        if (dev != NULL) {
1398                set_disk_named(device_name, NULL);
1399                set_disk_named(device_name, ped_disk_new(dev));
1400        }
1401        activate_exception_handler();
1402        unchange_named(device_name);
1403        oprintf("OK\n");
1404}
1405
1406void
1407command_partitions()
1408{
1409        PedPartition *part;
1410        PedConstraint *creation_constraint;
1411        PedSector grain_size;
1412        scan_device_name();
1413        if (dev == NULL)
1414                critical_error("The device %s is not opened.", device_name);
1415        log("command_partitions()");
1416        open_out();
1417        oprintf("OK\n");
1418        deactivate_exception_handler();
1419        if (disk == NULL) {
1420                log("No partitions");
1421                /* No label, hence no partitions.  When there is a
1422                   label, there is at least one partition because the
1423                   free space counts as a partition. */
1424                oprintf("\n");
1425                activate_exception_handler();
1426                return;
1427        }
1428        if (has_extended_partition(disk))
1429                minimize_extended_partition(disk);
1430        creation_constraint = partition_creation_constraint(dev);
1431        grain_size = creation_constraint->start_align->grain_size;
1432        ped_constraint_destroy(creation_constraint);
1433        for (part = NULL;
1434             NULL != (part = ped_disk_next_partition(disk, part));) {
1435                char *part_info;
1436                if (PED_PARTITION_EXTENDED & part->type)
1437                        continue;
1438                if (PED_PARTITION_METADATA & part->type)
1439                        continue;
1440                /* Undoubtedly the following operator is a hack.
1441                   Libparted tries to align the partitions at
1442                   appropriate boundaries but despite this it sometimes
1443                   reports free spaces due to aligning and even
1444                   allows creation of unaligned partitions in these
1445                   free spaces.  I am not sure if this is a bug or a
1446                   feature of libparted. */
1447                if (PED_PARTITION_FREESPACE & part->type
1448                    && ped_disk_type_check_feature(disk->type,
1449                                                   PED_DISK_TYPE_EXTENDED)
1450                    && ((part->geom).length
1451                        < dev->bios_geom.sectors * grain_size))
1452                        continue;
1453                /* Another hack :) */
1454                if (0 == strcmp(disk->type->name, "dvh")
1455                    && PED_PARTITION_LOGICAL & part->type)
1456                        continue;
1457                part_info = partition_info(disk, part);
1458                oprintf("%s\n", part_info);
1459                free(part_info);
1460        }
1461        log("Partitions printed");
1462        /* An empty line after the last partition */
1463        oprintf("\n");
1464        activate_exception_handler();
1465}
1466
1467void
1468command_partition_info()
1469{
1470        char *id;
1471        PedPartition *part;
1472        scan_device_name();
1473        if (dev == NULL)
1474                critical_error("The device %s is not opened.", device_name);
1475        log("command_partition_info()");
1476        open_out();
1477        if (1 != iscanf("%as", &id))
1478                critical_error("Expected partition id");
1479        log("command_partition_info: info for partition with id %s", id);
1480        part = partition_with_id(disk, id);
1481        oprintf("OK\n");
1482        deactivate_exception_handler();
1483        if (part == NULL) {
1484                log("command_partition_info: no such a partitions");
1485                oprintf("\n");
1486        } else {
1487                char *part_info;
1488                log("command_partition_info: partition found");
1489                part_info = partition_info(disk, part);
1490                oprintf("%s\n", part_info);
1491                free(part_info);
1492        }
1493        free(id);
1494        activate_exception_handler();
1495}
1496
1497void
1498command_get_chs()
1499{
1500        char *id;
1501        PedPartition *part;
1502        scan_device_name();
1503        if (dev == NULL)
1504                critical_error("The device %s is not opened.", device_name);
1505        log("command_get_chs()");
1506        open_out();
1507        if (1 != iscanf("%as", &id))
1508                critical_error("Expected partition id");
1509        log("command_get_chs: Cyl/Head/Sec for partition with id %s", id);
1510        part = partition_with_id(disk, id);
1511        oprintf("OK\n");
1512        deactivate_exception_handler();
1513        if (part == NULL) {
1514                log("command_get_chs: no such a partitions");
1515                oprintf("\n");
1516        } else {
1517                /* TODO: there is the same code in dump_info */
1518                long long cylinder_size, track_size;
1519                long long start, end;
1520                long long cyl_start, cyl_end;
1521                long long head_start, head_end;
1522                long long sec_start, sec_end;
1523                log("command_get_chs: partition found");
1524                track_size = dev->bios_geom.sectors;
1525                cylinder_size = track_size * dev->bios_geom.heads;
1526                start = (part->geom).start;
1527                end = (part->geom).end;
1528                cyl_start = start / cylinder_size;
1529                cyl_end = end / cylinder_size;
1530                start = start % cylinder_size;
1531                end = end % cylinder_size;
1532                head_start = start / track_size;
1533                head_end = end / track_size;
1534                sec_start = start % track_size;
1535                sec_end = end % track_size;
1536                oprintf("%lli\t%lli\t%lli\t%lli\t%lli\t%lli\n",
1537                        cyl_start, head_start, sec_start,
1538                        cyl_end, head_end, sec_end);
1539        }
1540        free(id);
1541        activate_exception_handler();
1542}
1543
1544void
1545command_label_types()
1546{
1547        PedDiskType *type = NULL;
1548        scan_device_name();
1549        if (dev == NULL)
1550                critical_error("The device %s is not opened.", device_name);
1551        log("command_label_types()");
1552        open_out();
1553        oprintf("OK\n");
1554        deactivate_exception_handler();
1555        while (NULL != (type = ped_disk_type_get_next(type))) {
1556                oprintf("%s\n", type->name);
1557        }
1558        oprintf("\n");
1559        activate_exception_handler();
1560}
1561
1562void
1563command_valid_flags()
1564{
1565        char *id;
1566        PedPartition *part;
1567        PedPartitionFlag flag;
1568        scan_device_name();
1569        if (dev == NULL)
1570                critical_error("The device %s is not opened.", device_name);
1571        log("command_valid_flags()");
1572        open_out();
1573        if (1 != iscanf("%as", &id))
1574                critical_error("Expected partition id");
1575        part = partition_with_id(disk, id);
1576        oprintf("OK\n");
1577        deactivate_exception_handler();
1578        if (part == NULL || !ped_partition_is_active(part)) {
1579                log("No such active partition: %s", id);
1580        } else {
1581                log("Partition found (%s)", id);
1582                for (flag = 0; 0 != (flag = ped_partition_flag_next(flag));)
1583                        if (ped_partition_is_flag_available(part, flag))
1584                                oprintf("%s\n",
1585                                        ped_partition_flag_get_name(flag));
1586        }
1587        oprintf("\n");
1588        free(id);
1589        activate_exception_handler();
1590}
1591
1592void
1593command_get_flags()
1594{
1595        char *id;
1596        PedPartition *part;
1597        PedPartitionFlag flag;
1598        scan_device_name();
1599        if (dev == NULL)
1600                critical_error("The device %s is not opened.", device_name);
1601        log("command_get_flags()");
1602        open_out();
1603        if (1 != iscanf("%as", &id))
1604                critical_error("Expected partition id");
1605        part = partition_with_id(disk, id);
1606        if (part == NULL || !ped_partition_is_active(part))
1607                critical_error("No such active partition: %s", id);
1608        log("Partition found (%s)", id);
1609        oprintf("OK\n");
1610        deactivate_exception_handler();
1611        for (flag = 0; 0 != (flag = ped_partition_flag_next(flag));)
1612                if (ped_partition_is_flag_available(part, flag)
1613                    && ped_partition_get_flag(part, flag))
1614                        oprintf("%s\n", ped_partition_flag_get_name(flag));
1615        oprintf("\n");
1616        free(id);
1617        activate_exception_handler();
1618}
1619
1620void
1621command_set_flags()
1622{
1623        char *id, *str;
1624        PedPartition *part;
1625        PedPartitionFlag first, last, flag;
1626        bool *states;
1627        scan_device_name();
1628        if (dev == NULL)
1629                critical_error("The device %s is not opened.", device_name);
1630        log("command_set_flags()");
1631        change_named(device_name);
1632        open_out();
1633        if (1 != iscanf("%as", &id))
1634                critical_error("Expected partition id");
1635        part = partition_with_id(disk, id);
1636        if (part == NULL || !ped_partition_is_active(part))
1637                critical_error("No such active partition: %s", id);
1638        log("Partition found (%s)", id);
1639        oprintf("OK\n");
1640        deactivate_exception_handler();
1641        first = ped_partition_flag_next(0);
1642        last = first - 1;
1643        for (flag = first; flag != 0; flag = ped_partition_flag_next(flag))
1644                last = flag;
1645        states = malloc(sizeof(bool[last - first + 1]));
1646        for (flag = first; flag <= last; flag++)
1647                states[flag - first] = false;
1648        while (1) {
1649                iscan_line(&str, 1);
1650                if (!str)
1651                        critical_error("No data in infifo!");
1652                if (!strcmp(str, "NO_MORE"))
1653                        break;
1654                log("Processing flag %s", str);
1655                flag = ped_partition_flag_get_by_name(str);
1656                if (flag >= first && flag <= last) {
1657                        log("The flag set true.");
1658                        states[flag - first] = true;
1659                }
1660                free(str);
1661        }
1662        free(str);
1663        for (flag = 0; 0 != (flag = ped_partition_flag_next(flag));)
1664                if (ped_partition_is_flag_available(part, flag))
1665                        ped_partition_set_flag(part, flag,
1666                                               states[flag - first]);
1667        free(states);
1668        activate_exception_handler();
1669        free(id);
1670}
1671
1672void
1673command_uses_names()
1674{
1675        scan_device_name();
1676        if (dev == NULL)
1677                critical_error("The device %s is not opened.", device_name);
1678        log("command_uses_names()");
1679        open_out();
1680        oprintf("OK\n");
1681        deactivate_exception_handler();
1682        if (ped_disk_type_check_feature(disk->type,
1683                                        PED_DISK_TYPE_PARTITION_NAME))
1684                oprintf("yes\n");
1685        else
1686                oprintf("no\n");
1687        activate_exception_handler();
1688}
1689
1690void
1691command_set_name()
1692{
1693        char *id, *name;
1694        PedPartition *part;
1695        scan_device_name();
1696        if (dev == NULL)
1697                critical_error("The device %s is not opened.", device_name);
1698        log("command_set_name()");
1699        change_named(device_name);
1700        if (!ped_disk_type_check_feature(disk->type,
1701                                         PED_DISK_TYPE_PARTITION_NAME))
1702                critical_error("This label doesn't support partition names.");
1703        if (1 != iscanf("%as", &id))
1704                critical_error("Expected partition id");
1705        part = partition_with_id(disk, id);
1706        if (part == NULL || !ped_partition_is_active(part))
1707                critical_error("No such active partition: %s", id);
1708        log("Partition found (%s)", id);
1709        iscan_line(&name, 0);
1710        if (!name)
1711                critical_error("No data in infifo!");
1712        log("Changing name to %s", name);
1713        open_out();
1714        oprintf("OK\n");
1715        deactivate_exception_handler();
1716        ped_partition_set_name(part, name);
1717        free(name);
1718        free(id);
1719        activate_exception_handler();
1720}
1721
1722void
1723command_get_max_primary()
1724{
1725        scan_device_name();
1726        if (dev == NULL)
1727                critical_error("The device %s is not opened.", device_name);
1728        log("command_get_max_primary()");
1729        open_out();
1730        oprintf("OK\n");
1731        deactivate_exception_handler();
1732        if (disk != NULL && disk->type != NULL)
1733                oprintf("%d\n",
1734                        ped_disk_get_max_primary_partition_count(disk));
1735        else
1736                oprintf("\n");
1737        activate_exception_handler();
1738}
1739
1740void
1741command_uses_extended()
1742{
1743        scan_device_name();
1744        if (dev == NULL)
1745                critical_error("The device %s is not opened.", device_name);
1746        log("command_uses_extended()");
1747        open_out();
1748        oprintf("OK\n");
1749        deactivate_exception_handler();
1750        if (disk != NULL && disk->type != NULL
1751            && ped_disk_type_check_feature(disk->type, PED_DISK_TYPE_EXTENDED)
1752            && 0 != strcmp(disk->type->name, "dvh"))
1753                oprintf("yes\n");
1754        else
1755                oprintf("no\n");
1756        activate_exception_handler();
1757}
1758
1759void
1760command_file_system_types()
1761{
1762        PedFileSystemType *type;
1763        scan_device_name();
1764        if (dev == NULL)
1765                critical_error("The device %s is not opened.", device_name);
1766        log("command_file_system_types()");
1767        open_out();
1768        oprintf("OK\n");
1769        deactivate_exception_handler();
1770        for (type = NULL;
1771             NULL != (type = ped_file_system_type_get_next(type));)
1772                oprintf("%s\n", type->name);
1773        oprintf("\n");
1774        activate_exception_handler();
1775}
1776
1777void
1778command_get_file_system()
1779{
1780        char *id;
1781        PedPartition *part;
1782        PedFileSystemType *fstype;
1783        scan_device_name();
1784        if (dev == NULL)
1785                critical_error("The device %s is not opened.", device_name);
1786        log("command_get_file_system()");
1787        open_out();
1788        if (1 != iscanf("%as", &id))
1789                critical_error("Expected partition id");
1790        log("command_get_file_system: File system for partition %s", id);
1791        part = partition_with_id(disk, id);
1792        oprintf("OK\n");
1793        if (named_partition_is_virtual(device_name,
1794                                       part->geom.start, part->geom.end)) {
1795                oprintf("none\n");
1796        } else {
1797                deactivate_exception_handler();
1798                fstype = ped_file_system_probe(&(part->geom));
1799                if (fstype == NULL) {
1800                        oprintf("none\n");
1801                } else {
1802                        if (0 == strncmp(fstype->name, "linux-swap", 10))
1803                                oprintf("linux-swap\n");
1804                        else
1805                                oprintf("%s\n", fstype->name);
1806                }
1807                free(id);
1808                activate_exception_handler();
1809        }
1810}
1811
1812void
1813command_change_file_system()
1814{
1815        char *id;
1816        PedPartition *part;
1817        char *s_fstype;
1818        PedFileSystemType *fstype;
1819        PedPartitionFlag flag;
1820        scan_device_name();
1821        if (dev == NULL)
1822                critical_error("The device %s is not opened.", device_name);
1823        open_out();
1824        if (2 != iscanf("%as %as", &id, &s_fstype))
1825                critical_error("Expected partition id and file system");
1826        log("command_change_file_system(%s,%s)", id, s_fstype);
1827        part = partition_with_id(disk, id);
1828        if (part == NULL) {
1829                critical_error("Partition not found: %s", id);
1830        }
1831        free(id);
1832
1833        mangle_fstype_name(&s_fstype);
1834
1835        fstype = ped_file_system_type_get(s_fstype);
1836        if (fstype == NULL) {
1837                log("Filesystem %s not found, let's see if it is a flag",
1838                    s_fstype);
1839                flag = ped_partition_flag_get_by_name(s_fstype);
1840                if (ped_partition_is_flag_available(part, flag)) {
1841                        if (!ped_partition_get_flag(part, flag)) {
1842                                change_named(device_name);
1843                                ped_partition_set_flag(part, flag, 1);
1844                        } else
1845                                log("Flag %s already set", s_fstype);
1846                } else {
1847                        critical_error("Bad file system or flag type: %s",
1848                                       s_fstype);
1849                }
1850        } else {
1851                if (!((PED_PARTITION_FREESPACE | PED_PARTITION_METADATA |
1852                       PED_PARTITION_EXTENDED) & part->type) &&
1853                    fstype != part->fs_type) {
1854                        change_named(device_name);
1855                        ped_partition_set_system(part, fstype);
1856                } else
1857                        log("Already using filesystem %s", s_fstype);
1858        }
1859        free(s_fstype);
1860        oprintf("OK\n");
1861}
1862
1863void
1864command_new_label()
1865{
1866        PedDiskType *type;
1867        char *str, *device;
1868        scan_device_name();
1869        if (dev == NULL)
1870                critical_error("The device %s is not opened.", device_name);
1871        log("command_new_label()");
1872        change_named(device_name);
1873        open_out();
1874        if (1 != iscanf("%as", &str))
1875                critical_error("Expected label type");
1876        type = ped_disk_type_get(str);
1877        if (type == NULL)
1878                critical_error("Bad label type: %s", str);
1879        log("command_new_label: requested label with type %s", str);
1880        device = strdup(device_named(device_name)->path);
1881        /* The old partition table may have contained wrong
1882           Cylinder/Head/Sector geometry.  So it is not probably
1883           enough to change the partition table (i.e. `disk'). */
1884        set_disk_named(device_name, NULL);
1885        set_device_named(device_name, NULL);
1886        dev = ped_device_get(device);
1887        free(device);
1888        if (NULL == dev)
1889                critical_error("Cannot reopen %s", device_name);
1890        set_device_named(device_name, dev);
1891        log("command_new_label: creating");
1892        disk = ped_disk_new_fresh(dev, type);
1893        if (disk == NULL) {
1894                static char *only_cancel[] = { "Cancel", NULL };
1895                pseudo_exception("Error",
1896                                 "Can't create new disk label.", only_cancel);
1897        } else
1898                set_disk_named(device_name, disk);
1899        oprintf("OK\n");
1900        free(str);
1901}
1902
1903void
1904command_new_partition()
1905{
1906        char *s_type, *s_fs_type;
1907        PedPartitionType type;
1908        PedFileSystemType *fs_type;
1909        long long range_start, range_end;
1910        char *position;
1911        PedSector length;
1912        PedSector part_start, part_end;
1913        PedPartition *part;
1914        int n;
1915        scan_device_name();
1916        if (dev == NULL)
1917                critical_error("The device %s is not opened.", device_name);
1918        assert(disk != NULL);
1919        log("command_new_partition()");
1920        change_named(device_name);
1921        open_out();
1922        n = iscanf("%as %as %lli-%lli %as %lli", &s_type, &s_fs_type,
1923                   &range_start, &range_end, &position, &length);
1924        if (n != 6)
1925                critical_error
1926                    ("Expected: part_type file_system id position length");
1927
1928        if (!strcasecmp(s_type, "primary"))
1929                type = 0;
1930        else if (!strcasecmp(s_type, "logical"))
1931                type = PED_PARTITION_LOGICAL;
1932        else
1933                critical_error("Bad partition type: %s", s_type);
1934        log("requested partition with type %s", s_type);
1935        free(s_type);
1936
1937        mangle_fstype_name(&s_fs_type);
1938
1939        fs_type = ped_file_system_type_get(s_fs_type);
1940        if (fs_type == NULL)
1941                critical_error("Bad file system type: %s", s_fs_type);
1942        log("requested partition with file system %s", s_fs_type);
1943        free(s_fs_type);
1944
1945        if (!strcasecmp(position, "full")) {
1946                part_start = range_start / dev->sector_size;
1947                part_end = ((range_end - dev->sector_size + 1)
1948                            / dev->sector_size);
1949        } else if (!strcasecmp(position, "beginning")) {
1950                part_start = range_start / dev->sector_size;
1951                part_end = (range_start + length) / dev->sector_size;
1952        } else if (!strcasecmp(position, "end")) {
1953                part_start = (range_end - length) / dev->sector_size;
1954                part_end = ((range_end - dev->sector_size + 1)
1955                            / dev->sector_size);
1956        } else
1957                critical_error("Bad position: %s", position);
1958        free(position);
1959
1960        if (disk == NULL)
1961                critical_error("No opened device or no partition table");
1962
1963        if (type == 0 /* PED_PARTITION_PRIMARY */ )
1964                part = add_primary_partition(disk, fs_type,
1965                                             part_start, part_end);
1966        else
1967                part = add_logical_partition(disk, fs_type,
1968                                             part_start, part_end);
1969        oprintf("OK\n");
1970        deactivate_exception_handler();
1971        if (part) {
1972                char *part_info = partition_info(disk, part);
1973                oprintf("%s\n", part_info);
1974                free(part_info);
1975        } else
1976                oprintf("\n");
1977        activate_exception_handler();
1978}
1979
1980void
1981command_delete_partition()
1982{
1983        PedPartition *part;
1984        char *id;
1985        scan_device_name();
1986        if (dev == NULL)
1987                critical_error("The device %s is not opened.", device_name);
1988        log("command_delete_partition()");
1989        change_named(device_name);
1990        open_out();
1991        if (1 != iscanf("%as", &id))
1992                critical_error("Expected partition id");
1993        log("Deleting partition with id %s", id);
1994        part = partition_with_id(disk, id);
1995        if (part == NULL)
1996                log("No such partition");
1997        else {
1998                PedPartitionType type = part->type;
1999                log("Partition found");
2000                ped_disk_delete_partition(disk, part);
2001                if (type & PED_PARTITION_LOGICAL)
2002                        minimize_extended_partition(disk);
2003                log("Partition deleted");
2004        }
2005        oprintf("OK\n");
2006        free(id);
2007}
2008
2009void
2010command_resize_partition()
2011{
2012        PedPartition *part;
2013        char *id;
2014        long long new_size;
2015        PedSector start, end;
2016        scan_device_name();
2017        if (dev == NULL)
2018                critical_error("The device %s is not opened.", device_name);
2019        assert(disk != NULL);
2020        log("command_resize_partition()");
2021        change_named(device_name);
2022        open_out();
2023        if (1 != iscanf("%as", &id))
2024                critical_error("Expected partition id");
2025        log("Resizing partition with id %s", id);
2026        part = partition_with_id(disk, id);
2027        if (part == NULL)
2028                critical_error("No such partition");
2029        if (1 != iscanf(" %lli", &new_size))
2030                critical_error("Expected new size");
2031        log("New size: %lli", new_size);
2032        start = (part->geom).start;
2033        end = start + new_size / dev->sector_size - 1;
2034        if (named_partition_is_virtual(device_name,
2035                                       part->geom.start, part->geom.end)) {
2036                resize_partition(disk, part, start, end, false);
2037        } else {
2038                if (resize_partition(disk, part, start, end, true)) {
2039                        ped_disk_commit(disk);
2040                        unchange_named(device_name);
2041                }
2042        }
2043        oprintf("OK\n");
2044        oprintf("%lli-%lli\n", (part->geom).start * dev->sector_size,
2045                (part->geom).end * dev->sector_size + dev->sector_size - 1);
2046        free(id);
2047}
2048
2049void
2050command_virtual_resize_partition()
2051{
2052        PedPartition *part;
2053        char *id;
2054        long long new_size;
2055        PedSector start, end;
2056        scan_device_name();
2057        if (dev == NULL)
2058                critical_error("The device %s is not opened.", device_name);
2059        assert(disk != NULL);
2060        log("command_virtual_resize_partition()");
2061        change_named(device_name);
2062        open_out();
2063        if (1 != iscanf("%as", &id))
2064                critical_error("Expected partition id");
2065        log("Resizing partition with id %s", id);
2066        part = partition_with_id(disk, id);
2067        if (part == NULL)
2068                critical_error("No such partition");
2069        if (1 != iscanf(" %lli", &new_size))
2070                critical_error("Expected new size");
2071        log("New size: %lli", new_size);
2072        start = (part->geom).start;
2073        end = start + new_size / dev->sector_size - 1;
2074        /* ensure that the size is not less than the requested */
2075        do {
2076                resize_partition(disk, part, start, end, false);
2077                end = end + 1;
2078        } while ((part->geom).length * dev->sector_size < new_size);
2079        ped_disk_commit(disk);
2080        unchange_named(device_name);
2081        oprintf("OK\n");
2082        oprintf("%lli-%lli\n", (part->geom).start * dev->sector_size,
2083                (part->geom).end * dev->sector_size + dev->sector_size - 1);
2084        free(id);
2085}
2086
2087void
2088command_get_resize_range()
2089{
2090        char *id;
2091        PedPartition *part;
2092        PedFileSystem *fs;
2093        PedConstraint *constraint;
2094        PedGeometry *max_geom;
2095        long long max_size, min_size, current_size;
2096        scan_device_name();
2097        if (dev == NULL)
2098                critical_error("The device %s is not opened.", device_name);
2099        assert(disk != NULL);
2100        log("command_get_resize_range()");
2101        open_out();
2102        if (1 != iscanf("%as", &id))
2103                critical_error("Expected partition id");
2104        deactivate_exception_handler();
2105        part = partition_with_id(disk, id);
2106        if (part == NULL)
2107                critical_error("No such partition");
2108        if (!named_partition_is_virtual(device_name,
2109                                        part->geom.start, part->geom.end)) {
2110                fs = ped_file_system_open(&(part->geom));
2111                if (NULL != fs && (fs->geom->start < (part->geom).start
2112                                   || fs->geom->end > (part->geom).end)) {
2113                        ped_file_system_close(fs);
2114                        fs = NULL;
2115                } else if (NULL == fs
2116                           && NULL != ped_file_system_probe(&(part->geom))) {
2117                        oprintf("OK\n");
2118                        oprintf("\n");
2119                        free(id);
2120                        activate_exception_handler();
2121                        return;
2122                }
2123        } else {
2124                fs = NULL;
2125        }
2126        if (NULL != fs) {
2127                constraint = ped_file_system_get_resize_constraint(fs);
2128                ped_file_system_close(fs);
2129        } else {
2130                constraint = ped_constraint_any(disk->dev);
2131        }
2132        ped_geometry_set_start(constraint->start_range, (part->geom).start);
2133        ped_geometry_set_end(constraint->start_range, (part->geom).start);
2134        if (part->type & PED_PARTITION_LOGICAL)
2135                maximize_extended_partition(disk);
2136        max_geom = ped_disk_get_max_partition_geometry(disk, part, constraint);
2137        if (part->type & PED_PARTITION_LOGICAL)
2138                minimize_extended_partition(disk);
2139        min_size = constraint->min_size * dev->sector_size;
2140        current_size = (part->geom).length * dev->sector_size;
2141        if (max_geom)
2142                max_size = max_geom->length * dev->sector_size;
2143        else
2144                max_size = current_size;
2145        oprintf("OK\n");
2146        oprintf("%lli %lli %lli\n", min_size, current_size, max_size);
2147        if (max_geom)
2148                ped_geometry_destroy(max_geom);
2149        ped_constraint_destroy(constraint);
2150        /* TODO: Probably there are memory leaks because of constraints. */
2151        activate_exception_handler();
2152        free(id);
2153}
2154
2155void
2156command_get_virtual_resize_range()
2157{
2158        char *id;
2159        PedPartition *part;
2160        PedConstraint *constraint;
2161        PedGeometry *max_geom;
2162        long long max_size, min_size, current_size;
2163        scan_device_name();
2164        if (dev == NULL)
2165                critical_error("The device %s is not opened.", device_name);
2166        assert(disk != NULL);
2167        log("command_get_virtual_resize_range()");
2168        open_out();
2169        if (1 != iscanf("%as", &id))
2170                critical_error("Expected partition id");
2171        deactivate_exception_handler();
2172        part = partition_with_id(disk, id);
2173        if (part == NULL)
2174                critical_error("No such partition");
2175        constraint = ped_constraint_any(disk->dev);
2176        ped_geometry_set_start(constraint->start_range, (part->geom).start);
2177        ped_geometry_set_end(constraint->start_range, (part->geom).start);
2178        if (part->type & PED_PARTITION_LOGICAL)
2179                maximize_extended_partition(disk);
2180        max_geom = ped_disk_get_max_partition_geometry(disk, part, constraint);
2181        if (part->type & PED_PARTITION_LOGICAL)
2182                minimize_extended_partition(disk);
2183        min_size = constraint->min_size * dev->sector_size;
2184        current_size = (part->geom).length * dev->sector_size;
2185        if (max_geom)
2186                max_size = max_geom->length * dev->sector_size;
2187        else
2188                max_size = current_size;
2189        oprintf("OK\n");
2190        oprintf("%lli %lli %lli\n", min_size, current_size, max_size);
2191        if (max_geom)
2192                ped_geometry_destroy(max_geom);
2193        ped_constraint_destroy(constraint);
2194        /* TODO: Probably there are memory leaks because of constraints. */
2195        activate_exception_handler();
2196        free(id);
2197}
2198
2199void
2200command_get_label_type()
2201{
2202        log("command_get_label_type()");
2203        scan_device_name();
2204        open_out();
2205        oprintf("OK\n");
2206        deactivate_exception_handler();
2207        if ((disk == NULL) || (disk->type == NULL)
2208            || (disk->type->name == NULL)) {
2209                oprintf("unknown\n");
2210        } else {
2211                oprintf("%s\n", disk->type->name);
2212        }
2213        activate_exception_handler();
2214}
2215
2216void
2217command_is_busy()
2218{
2219        char *id;
2220        PedPartition *part;
2221        log("command_is_busy()");
2222        scan_device_name();
2223        if (dev == NULL)
2224                critical_error("The device %s is not opened.", device_name);
2225        open_out();
2226        if (1 != iscanf("%as", &id))
2227                critical_error("Expected partition id");
2228        log("command_is_busy: busy check for id %s", id);
2229        part = partition_with_id(disk, id);
2230        oprintf("OK\n");
2231        if (ped_partition_is_busy(part)) {
2232                oprintf("yes\n");
2233        } else {
2234                oprintf("no\n");
2235        }
2236        free(id);
2237}
2238
2239void
2240command_alignment_offset()
2241{
2242        char *id;
2243        PedPartition *part;
2244        PedAlignment *align;
2245        log("command_alignment_offset()");
2246        scan_device_name();
2247        if (dev == NULL)
2248                critical_error("The device %s is not opened.", device_name);
2249        open_out();
2250        if (1 != iscanf("%as", &id))
2251                critical_error("Expected partition id");
2252        part = partition_with_id(disk, id);
2253        oprintf("OK\n");
2254        if (alignment_of_device(dev) == ALIGNMENT_CYLINDER)
2255                /* None of this is useful when using cylinder alignment. */
2256                oprintf("0\n");
2257        else {
2258                align = ped_device_get_minimum_alignment(dev);
2259
2260                /* align->offset represents the offset of the lowest logical
2261                 * block on the disk from the disk's natural alignment,
2262                 * modulo the physical sector size (e.g. 4096 bytes), as a
2263                 * number of logical sectors (e.g. 512 bytes).  For a disk
2264                 * with 4096-byte physical sectors deliberately misaligned
2265                 * to make DOS-style 63-sector offsets work well, we would
2266                 * thus expect align->offset to be 1, as (1 + 63) * 512 /
2267                 * 4096 is an integer.
2268                 *
2269                 * To get the alignment offset of a *partition*, we thus
2270                 * need to start with align->offset (in bytes) plus the
2271                 * partition start position.
2272                 */
2273                oprintf("%lld\n",
2274                        ((align->offset + part->geom.start) *
2275                         dev->sector_size) %
2276                        dev->phys_sector_size);
2277
2278                ped_alignment_destroy(align);
2279        }
2280        free(id);
2281}
2282
2283void
2284make_fifo(char* name)
2285{
2286    int status;
2287    status = mkfifo(name, 0644);
2288    if ((status != 0))
2289            if (errno != EEXIST) {
2290                    perror("Cannot create FIFO");
2291                    exit(252);
2292            }
2293}
2294
2295void
2296make_fifos()
2297{
2298    make_fifo(infifo_name);
2299    make_fifo(outfifo_name);
2300    make_fifo(stopfifo_name);
2301} 
2302
2303int
2304write_pid_file()
2305{
2306        FILE *fd;
2307        int status;
2308        pid_t oldpid;
2309        if ((fd = fopen(pidfile_name, "a+")) == NULL)
2310                return -1;
2311
2312        status = fscanf(fd, "%d", &oldpid);
2313        if (status != 0 && status != EOF) {
2314                // If kill(oldpid, 0) == 0 the process is still alive
2315                // so we abort
2316                if (kill(oldpid, 0) == 0) {
2317                        fprintf(stderr, "Not starting: process %d still exists\n", oldpid);
2318                        fclose(fd);
2319                        exit(250);
2320                }
2321        }
2322
2323        // Truncate the pid file and continue
2324        freopen(pidfile_name, "w", fd);
2325     
2326        fprintf(fd, "%d", (int)(getpid()));
2327        fclose(fd);
2328        return 0;
2329}
2330
2331void
2332cleanup_and_die()
2333{
2334        if (unlink(pidfile_name) != 0)
2335                perror("Cannot unlink pid file");
2336        if (unlink(infifo_name) != 0)
2337                perror("Cannot unlink input FIFO");
2338        if (unlink(outfifo_name) != 0)
2339                perror("Cannot unlink output FIFO");
2340        if (unlink(stopfifo_name) != 0)
2341                perror("Cannot unlink stop FIFO");
2342}
2343
2344void
2345prnt_sig_hdlr(int signal)
2346{
2347        int status;
2348        switch(signal) {
2349                // SIGUSR1 signals that child is ready to take
2350                // requests (i.e. has finished initialisation)
2351                case SIGUSR1:
2352                    exit(0);
2353                    break;
2354                // We'll only get SIGCHLD if our child has pre-deceased us
2355                // In this case we should exit with its error code
2356                case SIGCHLD:
2357                    if (waitpid(-1, &status, WNOHANG) < 0)
2358                        exit(0);
2359                    if (WIFEXITED(status))
2360                        exit(WEXITSTATUS(status));
2361                    break;
2362                default:
2363                    break;
2364        }
2365}
2366
2367/**********************************************************************
2368   Main
2369**********************************************************************/
2370
2371void main_loop() __attribute__ ((noreturn));
2372
2373void
2374main_loop()
2375{
2376        char *str;
2377        int iteration = 1;
2378        set_alignment();
2379        while (1) {
2380                log("main_loop: iteration %i", iteration++);
2381                open_in();
2382                if (1 != iscanf("%as", &str))
2383                        critical_error("No data in infifo.");
2384                log("Read command: %s", str);
2385                /* Keep partman-command in sync with changes here. */
2386                if (!strcasecmp(str, "QUIT"))
2387                        command_quit();
2388                else if (!strcasecmp(str, "OPEN"))
2389                        command_open();
2390                else if (!strcasecmp(str, "CLOSE"))
2391                        command_close();
2392                else if (!strcasecmp(str, "OPENED"))
2393                        command_opened();
2394                else if (!strcasecmp(str, "VIRTUAL"))
2395                        command_virtual();
2396                else if (!strcasecmp(str, "DISK_UNCHANGED"))
2397                        command_disk_unchanged();
2398                else if (!strcasecmp(str, "IS_CHANGED"))
2399                        command_is_changed();
2400                else if (!strcasecmp(str, "DUMP"))
2401                        command_dump();
2402                else if (!strcasecmp(str, "COMMIT"))
2403                        command_commit();
2404                else if (!strcasecmp(str, "UNDO"))
2405                        command_undo();
2406                else if (!strcasecmp(str, "PARTITIONS"))
2407                        command_partitions();
2408                else if (!strcasecmp(str, "PARTITION_INFO"))
2409                        command_partition_info();
2410                else if (!strcasecmp(str, "GET_CHS"))
2411                        command_get_chs();
2412                else if (!strcasecmp(str, "LABEL_TYPES"))
2413                        command_label_types();
2414                else if (!strcasecmp(str, "VALID_FLAGS"))
2415                        command_valid_flags();
2416                else if (!strcasecmp(str, "GET_FLAGS"))
2417                        command_get_flags();
2418                else if (!strcasecmp(str, "SET_FLAGS"))
2419                        command_set_flags();
2420                else if (!strcasecmp(str, "SET_NAME"))
2421                        command_set_name();
2422                else if (!strcasecmp(str, "USES_NAMES"))
2423                        command_uses_names();
2424                else if (!strcasecmp(str, "GET_MAX_PRIMARY"))
2425                        command_get_max_primary();
2426                else if (!strcasecmp(str, "USES_EXTENDED"))
2427                        command_uses_extended();
2428                else if (!strcasecmp(str, "FILE_SYSTEM_TYPES"))
2429                        command_file_system_types();
2430                else if (!strcasecmp(str, "GET_FILE_SYSTEM"))
2431                        command_get_file_system();
2432                else if (!strcasecmp(str, "CHANGE_FILE_SYSTEM"))
2433                        command_change_file_system();
2434                else if (!strcasecmp(str, "NEW_LABEL"))
2435                        command_new_label();
2436                else if (!strcasecmp(str, "NEW_PARTITION"))
2437                        command_new_partition();
2438                else if (!strcasecmp(str, "DELETE_PARTITION"))
2439                        command_delete_partition();
2440                else if (!strcasecmp(str, "RESIZE_PARTITION"))
2441                        command_resize_partition();
2442                else if (!strcasecmp(str, "GET_RESIZE_RANGE"))
2443                        command_get_resize_range();
2444                /* these two functions are undocumented and should disappear */
2445                else if (!strcasecmp(str, "VIRTUAL_RESIZE_PARTITION"))
2446                        command_virtual_resize_partition();
2447                else if (!strcasecmp(str, "GET_VIRTUAL_RESIZE_RANGE"))
2448                        command_get_virtual_resize_range();
2449                else if (!strcasecmp(str, "GET_LABEL_TYPE"))
2450                        command_get_label_type();
2451                else if (!strcasecmp(str, "IS_BUSY"))
2452                        command_is_busy();
2453                else if (!strcasecmp(str, "ALIGNMENT_OFFSET"))
2454                        command_alignment_offset();
2455                else
2456                        critical_error("Unknown command %s", str);
2457                free(str);
2458                close_fifos_and_synchronise();
2459        }
2460}
2461
2462int
2463main(int argc, char *argv[])
2464{
2465        struct sigaction act, oldact;
2466        int i;
2467
2468        /* Close all extraneous file descriptors, including our pipe to
2469         * debconf.
2470         */
2471        for (i = 3; i < 256; ++i)
2472                close(i);
2473
2474        // Set up signal handling
2475        memset(&act,0,sizeof(struct sigaction));
2476        memset(&oldact,0,sizeof(struct sigaction));
2477        act.sa_handler = prnt_sig_hdlr;
2478        sigemptyset(&act.sa_mask);
2479
2480        // Set up signal handling for parent
2481        if  ((sigaction(SIGCHLD, &act, &oldact) < 0)
2482          || (sigaction(SIGUSR1, &act, &oldact) < 0))
2483        {
2484            fprintf(stderr, "Could not set up signal handling for parent\n");
2485            exit(251);
2486        }
2487     
2488        // The parent process should wait; we die once child is
2489        // initialised (signalled by a SIGUSR1)
2490        if (fork()) {
2491            while (1) { sleep(5); };
2492        }
2493
2494        // Set up signal handling for child
2495        if  ((sigaction(SIGCHLD, &oldact, NULL) < 0)
2496          || (sigaction(SIGUSR1, &oldact, NULL) < 0))
2497        {
2498            fprintf(stderr, "Could not set up signal handling for child\n");
2499            exit(250);
2500        }
2501
2502        // Continue as a daemon process
2503        logfile = fopen(logfile_name, "a+");
2504        if (logfile == NULL) {
2505                fprintf(stderr, "Cannot append to the log file\n");
2506                exit(255);
2507        }
2508        if (write_pid_file() != 0) {
2509                fprintf(stderr, "Cannot open pid file\n");
2510                exit(254);
2511        }
2512        if (atexit(cleanup_and_die) != 0) {
2513                fprintf(stderr, "Cannot set atexit routine\n");
2514                exit(253);
2515        }
2516        make_fifos();
2517        // Signal that we've finished initialising so that the parent process
2518        // can die and the shell scripts can continue
2519        kill(getppid(), SIGUSR1);
2520        ped_exception_set_handler(exception_handler);
2521        log("======= Starting the server");
2522        main_loop();
2523}
2524
2525/*
2526The following command can be used to format this file in a consistent
2527with codingstyle.txt way:
2528
2529indent 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
2530*/
2531
2532/*
2533Local variables:
2534indent-tabs-mode: nil
2535c-file-style: "linux"
2536c-font-lock-extra-types: ("FILE" "\\sw+_t" "bool" "Ped\\sw+")
2537End:
2538*/
Note: See TracBrowser for help on using the repository browser.