iPXE
monojob.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <string.h>
00027 #include <stdio.h>
00028 #include <errno.h>
00029 #include <ipxe/process.h>
00030 #include <ipxe/console.h>
00031 #include <ipxe/keys.h>
00032 #include <ipxe/job.h>
00033 #include <ipxe/monojob.h>
00034 #include <ipxe/timer.h>
00035 
00036 /** @file
00037  *
00038  * Single foreground job
00039  *
00040  */
00041 
00042 static int monojob_rc;
00043 
00044 static void monojob_close ( struct interface *intf, int rc ) {
00045         monojob_rc = rc;
00046         intf_restart ( intf, rc );
00047 }
00048 
00049 static struct interface_operation monojob_intf_op[] = {
00050         INTF_OP ( intf_close, struct interface *, monojob_close ),
00051 };
00052 
00053 static struct interface_descriptor monojob_intf_desc =
00054         INTF_DESC_PURE ( monojob_intf_op );
00055 
00056 struct interface monojob = INTF_INIT ( monojob_intf_desc );
00057 
00058 /**
00059  * Clear previously displayed message
00060  *
00061  * @v len               Length of previously displayed message
00062  */
00063 static void monojob_clear ( size_t len ) {
00064         unsigned int i;
00065 
00066         for ( i = 0 ; i < len ; i++ )
00067                 putchar ( '\b' );
00068         for ( i = 0 ; i < len ; i++ )
00069                 putchar ( ' ' );
00070         for ( i = 0 ; i < len ; i++ )
00071                 putchar ( '\b' );
00072 }
00073 
00074 /**
00075  * Wait for single foreground job to complete
00076  *
00077  * @v string            Job description to display, or NULL to be silent
00078  * @v timeout           Timeout period, in ticks (0=indefinite)
00079  * @ret rc              Job final status code
00080  */
00081 int monojob_wait ( const char *string, unsigned long timeout ) {
00082         struct job_progress progress;
00083         unsigned long last_check;
00084         unsigned long last_progress;
00085         unsigned long last_display;
00086         unsigned long now;
00087         unsigned long elapsed;
00088         unsigned long completed = 0;
00089         unsigned long scaled_completed;
00090         unsigned long scaled_total;
00091         unsigned int percentage;
00092         size_t clear_len = 0;
00093         int ongoing_rc;
00094         int key;
00095         int rc;
00096 
00097         if ( string )
00098                 printf ( "%s...", string );
00099         monojob_rc = -EINPROGRESS;
00100         last_check = last_progress = last_display = currticks();
00101         while ( monojob_rc == -EINPROGRESS ) {
00102 
00103                 /* Allow job to progress */
00104                 step();
00105                 now = currticks();
00106 
00107                 /* Continue until a timer tick occurs (to minimise
00108                  * time wasted checking for progress and keypresses).
00109                  */
00110                 elapsed = ( now - last_check );
00111                 if ( ! elapsed )
00112                         continue;
00113                 last_check = now;
00114 
00115                 /* Check for keypresses */
00116                 if ( iskey() ) {
00117                         key = getchar();
00118                         if ( key == CTRL_C ) {
00119                                 monojob_rc = -ECANCELED;
00120                                 break;
00121                         }
00122                 }
00123 
00124                 /* Monitor progress */
00125                 ongoing_rc = job_progress ( &monojob, &progress );
00126 
00127                 /* Reset timeout if progress has been made */
00128                 if ( completed != progress.completed )
00129                         last_progress = now;
00130                 completed = progress.completed;
00131 
00132                 /* Check for timeout, if applicable */
00133                 elapsed = ( now - last_progress );
00134                 if ( timeout && ( elapsed >= timeout ) ) {
00135                         monojob_rc = ( ongoing_rc ? ongoing_rc : -ETIMEDOUT );
00136                         break;
00137                 }
00138 
00139                 /* Display progress, if applicable */
00140                 elapsed = ( now - last_display );
00141                 if ( string && ( elapsed >= TICKS_PER_SEC ) ) {
00142                         monojob_clear ( clear_len );
00143                         /* Normalise progress figures to avoid overflow */
00144                         scaled_completed = ( progress.completed / 128 );
00145                         scaled_total = ( progress.total / 128 );
00146                         if ( scaled_total ) {
00147                                 percentage = ( ( 100 * scaled_completed ) /
00148                                                scaled_total );
00149                                 clear_len = printf ( "%3d%%", percentage );
00150                         } else {
00151                                 printf ( "." );
00152                                 clear_len = 0;
00153                         }
00154                         if ( progress.message[0] ) {
00155                                 clear_len += printf ( " [%s]",
00156                                                       progress.message );
00157                         }
00158                         last_display = now;
00159                 }
00160         }
00161         rc = monojob_rc;
00162         monojob_close ( &monojob, rc );
00163 
00164         monojob_clear ( clear_len );
00165         if ( string ) {
00166                 if ( rc ) {
00167                         printf ( " %s\n", strerror ( rc ) );
00168                 } else {
00169                         printf ( " ok\n" );
00170                 }
00171         }
00172 
00173         return rc;
00174 }