File indexing completed on 2024-04-28 04:49:43

0001 /*
0002     SPDX-FileCopyrightText: 1998-2007 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "k3bgrowisofshandler.h"
0007 #include "k3bjob.h"
0008 #include "k3bcore.h"
0009 #include "k3bglobalsettings.h"
0010 #include "k3bdevicehandler.h"
0011 #include "k3b_i18n.h"
0012 
0013 #include <QDebug>
0014 #include <QLocale>
0015 #include <QTimer>
0016 
0017 #include <errno.h>
0018 #include <string.h>
0019 
0020 
0021 class K3b::GrowisofsHandler::Private
0022 {
0023 public:
0024     int lastBuffer;
0025     int lastDeviceBuffer;
0026 };
0027 
0028 
0029 K3b::GrowisofsHandler::GrowisofsHandler( QObject* parent )
0030     : QObject( parent ),
0031       m_mediaType(Device::MEDIA_DVD_ALL)
0032 {
0033     d = new Private;
0034     reset();
0035 }
0036 
0037 
0038 K3b::GrowisofsHandler::~GrowisofsHandler()
0039 {
0040     delete d;
0041 }
0042 
0043 
0044 void K3b::GrowisofsHandler::reset( K3b::Device::Device* dev, bool dao )
0045 {
0046     m_device = dev;
0047     m_error = ERROR_UNKNOWN;
0048     m_dao = dao;
0049     d->lastBuffer = 0;
0050     d->lastDeviceBuffer = 0;
0051 }
0052 
0053 
0054 void K3b::GrowisofsHandler::handleStart()
0055 {
0056 //  QTimer::singleShot( 2000, this, SLOT(slotCheckBufferStatus()) );
0057 }
0058 
0059 
0060 void K3b::GrowisofsHandler::handleLine( const QString& line )
0061 {
0062     int pos = 0;
0063 
0064     if( line.startsWith( ":-[" ) ) {
0065         // Error
0066 
0067         if( line.contains( "ASC=30h" ) )
0068             m_error = ERROR_MEDIA;
0069 
0070         // :-[ PERFORM OPC failed with SK=3h/ASC=73h/ASCQ=03h
0071         else if( line.startsWith( ":-[ PERFORM OPC failed" ) )
0072             emit infoMessage( i18n("OPC failed. Please try writing speed 1x."), K3b::Job::MessageError );
0073 
0074         else if( line.startsWith( ":-[ Failed to change write speed" ) ) {
0075             m_error = ERROR_SPEED_SET_FAILED;
0076         }
0077     }
0078     else if( line.startsWith( ":-(" ) ) {
0079         if( line.contains( "No space left on device" ) )
0080             m_error = ERROR_OVERSIZE;
0081 
0082         else if( line.contains( "blocks are free" ) && line.contains( "to be written" ) ) {
0083             m_error = ERROR_OVERSIZE;
0084             if( k3bcore->globalSettings()->overburn() )
0085                 emit infoMessage( i18n("Trying to write more than the official disk capacity"), K3b::Job::MessageWarning );
0086         }
0087 
0088         else if( line.startsWith( ":-( unable to anonymously mmap" ) ) {
0089             m_error = ERROR_MEMLOCK;
0090         }
0091 
0092         else if( line.startsWith( ":-( write failed" ) ) {
0093             m_error = ERROR_WRITE_FAILED;
0094         }
0095 
0096         // :-( attempt to re-run with -dvd-compat -dvd-compat to engage DAO or apply full blanking procedure
0097         else if( !m_dao &&
0098                  ( line.contains( "engage DAO" ) || line.contains( "media is not formatted or unsupported" ) ) )
0099             emit infoMessage( i18n("Please try again with writing mode DAO."), K3b::Job::MessageError );
0100 
0101         else
0102             emit infoMessage( line, K3b::Job::MessageError );
0103     }
0104     else if( line.startsWith( "PERFORM OPC" ) ) {
0105         m_error = ERROR_OPC;
0106     }
0107     else if( line.contains( "flushing cache" ) ) {
0108         // here is where we already should stop querying the buffer fill
0109         // since the device is only used there so far...
0110         m_device = 0;
0111 
0112         emit flushingCache();
0113         emit newSubTask( i18n("Flushing Cache")  );
0114         emit infoMessage( i18n("Flushing the cache may take some time."), K3b::Job::MessageInfo );
0115     }
0116 
0117     // FIXME: I think this starts with dev->blockDeviceName() so we could improve parsing with:
0118     //        if( line.startsWith( dev->blockDeviceName() ) ) {
0119     //              line = line.mid( dev->blockDeviceName().length() );
0120     //              if( line.startsWith( "closing.....
0121 
0122     else if( line.contains( "closing track" ) ) {
0123         emit newSubTask( i18n("Closing Track")  );
0124     }
0125     else if( line.contains( "closing disc" ) ) {
0126         emit newSubTask( i18n("Closing Disk")  );
0127     }
0128     else if( line.contains( "updating RMA" ) ) {
0129         emit newSubTask( i18n("Updating RMA") );
0130         emit infoMessage( i18n("Updating RMA..."), K3b::Job::MessageInfo );
0131     }
0132     else if (line.contains("closing session")) {
0133         emit newSubTask(i18n("Closing Session"));
0134         emit infoMessage(i18n("Closing Session..."), K3b::Job::MessageInfo);
0135     }
0136     else if( line.contains( "writing lead-out" ) ) {
0137         emit newSubTask( i18n("Writing Lead-out") );
0138         emit infoMessage( i18n("Writing the lead-out may take some time."), K3b::Job::MessageInfo );
0139     }
0140     else if( line.contains( "Quick Grow" ) ) {
0141         emit infoMessage( i18n("Removing reference to lead-out."), K3b::Job::MessageInfo );
0142     }
0143     else if( line.contains( "copying volume descriptor" ) ) {
0144         emit infoMessage( i18n("Modifying ISO 9660 volume descriptor"), K3b::Job::MessageInfo );
0145     }
0146     else if( line.contains( "FEATURE 21h is not on" ) ) {
0147         if( !m_dao ) {
0148             // FIXME: it's not only the writer. It may be the media: something like does not support it with this media
0149             //        da war was mit: wenn einmal formattiert, dann geht nur noch dao oder wenn einmal als overwrite
0150             //        formattiert, dann nur noch dao oder sowas
0151             emit infoMessage( i18n("Writing mode Incremental Streaming not available"), K3b::Job::MessageWarning );
0152             emit infoMessage( i18n("Engaging DAO"), K3b::Job::MessageWarning );
0153         }
0154     }
0155     else if( ( pos = line.indexOf( "Current Write Speed" ) ) > 0 ) {
0156         // parse write speed
0157         // /dev/sr0: "Current Write Speed" is 2.4x1385KBps for DVD or 4.1x4496KBps for BD
0158 
0159         pos += 24;
0160         int endPos = line.indexOf( 'x', pos+1 );
0161         bool ok = true;
0162         double speed = line.mid( pos, endPos-pos ).toDouble(&ok);
0163         if (ok) {
0164             emit infoMessage(i18n("Writing speed: %1 KB/s (%2x)",
0165                              int(speed * double(m_mediaType == Device::MEDIA_DVD_ALL ? Device::SPEED_FACTOR_DVD : Device::SPEED_FACTOR_BD)),
0166                              QLocale::system().toString(speed)), K3b::Job::MessageInfo);
0167         } else
0168             qDebug() << "(K3b::GrowisofsHandler) parsing error: '" << line.mid( pos, endPos-pos ) << "'";
0169     }
0170     else if( (pos = line.indexOf( "RBU" )) > 0 ) {
0171 
0172         // FIXME: use QRegExp
0173 
0174         // parse ring buffer fill for growisofs >= 6.0
0175         pos += 4;
0176         int endPos = line.indexOf( '%', pos+1 );
0177         bool ok = true;
0178         double val = line.mid( pos, endPos-pos ).toDouble( &ok );
0179         if( ok ) {
0180             int newBuffer = (int)(val+0.5);
0181             if( newBuffer != d->lastBuffer ) {
0182                 d->lastBuffer = newBuffer;
0183                 emit buffer( newBuffer );
0184             }
0185 
0186             // device buffer for growisofs >= 7.0
0187             pos = line.indexOf( "UBU", pos );
0188             endPos = line.indexOf( '%', pos+5 );
0189             if( pos > 0 ) {
0190                 pos += 4;
0191                 val = line.mid( pos, endPos-pos ).toDouble( &ok );
0192                 if( ok ) {
0193                     int newBuffer = (int)(val+0.5);
0194                     if( newBuffer != d->lastDeviceBuffer ) {
0195                         d->lastDeviceBuffer = newBuffer;
0196                         emit deviceBuffer( newBuffer );
0197                     }
0198                 }
0199             }
0200         }
0201         else
0202             qDebug() << "(K3b::GrowisofsHandler) failed to parse ring buffer fill from '" << line.mid( pos, endPos-pos ) << "'";
0203     }
0204 
0205     else {
0206         qDebug() << "(growisofs) " << line;
0207     }
0208 }
0209 
0210 
0211 void K3b::GrowisofsHandler::handleExit( int exitCode )
0212 {
0213     switch( m_error ) {
0214     case ERROR_MEDIA:
0215         emit infoMessage( i18n("K3b detected a problem with the medium."), K3b::Job::MessageError );
0216         emit infoMessage( i18n("Please try another brand of media, preferably one explicitly recommended by your writer's vendor."), K3b::Job::MessageError );
0217         emit infoMessage( i18n("Report the problem if it persists anyway."), K3b::Job::MessageError );
0218         break;
0219 
0220     case ERROR_OVERSIZE:
0221         if( k3bcore->globalSettings()->overburn() )
0222             emit infoMessage( i18n("Data did not fit on disk."), K3b::Job::MessageError );
0223         else
0224             emit infoMessage( i18n("Data does not fit on disk."), K3b::Job::MessageError );
0225         break;
0226 
0227     case ERROR_SPEED_SET_FAILED:
0228         emit infoMessage( i18n("Unable to set writing speed."), K3b::Job::MessageError );
0229         emit infoMessage( i18n("Please try again with the 'ignore speed' setting."), K3b::Job::MessageError );
0230         break;
0231 
0232     case ERROR_OPC:
0233         emit infoMessage( i18n("Optimum Power Calibration failed."), K3b::Job::MessageError );
0234         emit infoMessage( i18n("Try adding '-use-the-force-luke=noopc' to the "
0235                                "growisofs user parameters in the K3b settings."), K3b::Job::MessageError );
0236         break;
0237 
0238     case ERROR_MEMLOCK:
0239         emit infoMessage( i18n("Unable to allocate software buffer."), K3b::Job::MessageError );
0240         emit infoMessage( i18n("This error is caused by the low memorylocked resource limit."), K3b::Job::MessageError );
0241         emit infoMessage( i18n("It can be solved by issuing the command 'ulimit -l unlimited'..."), K3b::Job::MessageError );
0242         emit infoMessage( i18n("...or by lowering the used software buffer size in the advanced K3b settings."), K3b::Job::MessageError );
0243         break;
0244 
0245     case ERROR_WRITE_FAILED:
0246         emit infoMessage( i18n("Write error"), K3b::Job::MessageError );
0247         break;
0248 
0249     default:
0250 
0251         //
0252         // The growisofs error codes:
0253         //
0254         // 128 + errno: fatal error upon program startup
0255         // errno      : fatal error during recording
0256         //
0257 
0258         if( exitCode > 128 ) {
0259             // for now we just emit a message with the error
0260             // in the future when I know more about what kinds of errors may occur
0261             // we will enhance this
0262             emit infoMessage( i18n( "Fatal error at startup: %1", QString::fromLocal8Bit( ::strerror( exitCode-128 ) ) ),
0263                               K3b::Job::MessageError );
0264         }
0265         else if( exitCode == 1 ) {
0266             // Doku says: warning at exit
0267             // Example: mkisofs error
0268             //          unable to reload
0269             // So basically this is just for mkisofs failure since we do not let growisofs reload the media
0270             emit infoMessage( i18n("Warning at exit: (1)"), K3b::Job::MessageError );
0271             emit infoMessage( i18n("Most likely mkisofs failed in some way."), K3b::Job::MessageError );
0272         }
0273         else {
0274             emit infoMessage( i18n( "Fatal error during recording: %1", QString::fromLocal8Bit( ::strerror(exitCode) ) ),
0275                               K3b::Job::MessageError );
0276         }
0277     }
0278 
0279     reset();
0280 }
0281 
0282 
0283 void K3b::GrowisofsHandler::slotCheckBufferStatus()
0284 {
0285     connect( K3b::Device::sendCommand( K3b::Device::DeviceHandler::CommandBufferCapacity, m_device ),
0286              SIGNAL(finished(K3b::Device::DeviceHandler*)),
0287              this,
0288              SLOT(slotCheckBufferStatusDone(K3b::Device::DeviceHandler*)) );
0289 }
0290 
0291 
0292 void K3b::GrowisofsHandler::slotCheckBufferStatusDone( K3b::Device::DeviceHandler* dh )
0293 {
0294     if( dh->success() && dh->bufferCapacity() > 0 ) {
0295         emit deviceBuffer( 100 * (dh->bufferCapacity() - dh->availableBufferCapacity() ) / dh->bufferCapacity() );
0296         QTimer::singleShot( 500, this, SLOT(slotCheckBufferStatus()) );
0297     }
0298     else {
0299         qDebug() << "(K3b::GrowisofsHandler) stopping buffer check.";
0300     }
0301 }
0302 
0303 void K3b::GrowisofsHandler::setMediaType(Device::MediaType mediaType) 
0304 {
0305     m_mediaType = mediaType;
0306 }
0307 
0308 #include "moc_k3bgrowisofshandler.cpp"