MS Office: PDF-Drucker

Gebraucht wird ein PDF-Drucker für (hüst) Word 2000. Nach dem fälligen Update von Windows XP nach Windows 7 (64-Bit) will das alte Adobe Professional 7 keinen PDF-Drucker mehr zur Verfügung stellen. Es ist weiterhin gewünscht, dass der neue PDF-Drucker sich wie der Konverter von Adobe Professional verhält — also nicht über den Druckdialog sondern über ein Icon/Menü gestartet wird. Weiterhin soll das erzeugte PDF in der Verzeichnisstruktur im selben Ordner und mit dem selben Dateinamen (bis auf die Endung .pdf) wie das .doc gespeichert werden.

Das Starten des Drucks über einen separaten Menüpunkt lässt sich leicht mit einem Makro und der Methode ActiveDocument.PrintOut() bewerkstelligen. Schwieriger ist die Übergabe eines Dateinamens an das Drucksystem, da dieses keine Informationen über das zu druckende Dokument erhält/benötigt. Ich habe schliesslich den PDF-Drucker von bioPDF benutzt, da er, im Gegensatz zu vielen anderen, über ein API verfügt. Wie sich allerdings herausstellte, hat MS Office <= 2003 einen Bug, der die native Nutzung des API verhindert. bioPDF stellt aber eine weitere Möglichkeit bereit den Druckvorgang zu steuern, nämlich eine config.exe die eine runonce.ini erzeugt — sozusagen eine Konfiguration für genau einen (den nächsten) Druckjob.

Das folgende VB-Skript tut schliesslich alles was ich eben beschrieben habe (und noch ein kleines Bisschen mehr):

Sub printPDF()

    Dim sPath As String
    Dim sName As String
    Dim sFullName As String
    Dim sPdfFileName As String
    Dim o As Document
    Dim sCurrentPrinterName As String

    Set o = ActiveDocument

    sCurrentPrinterName = ActivePrinter

    ActivePrinter = "PDF Writer - bioPDF"

    If Len(o.Path) = 0 Then
        o.Save
    End If

    sName = CreateObject("scripting.filesystemobject").getBaseName(o.Name)
    sPath = o.Path
    sFullName = o.FullName
    sPdfFileName = sPath & "\" & sName & ".pdf"

    Dim cmd As String

    cmd = "C:\Program Files\bioPDF\PDF Writer\API\EXE\config.exe "

    Shell cmd & "/S Output " & sPdfFileName, vbHide
    Shell cmd & "/S showpdf " & "no", vbHide
    Shell cmd & "/S openfolder " & "no", vbHide

    o.PrintOut

    ActivePrinter = sCurrentPrinterName
End Sub

OpenIndiana: Zonen im privaten Netzwerk

Ziel ist es unter OpenIndiana (Oi_151a7) zwei Zonen zu konfigurieren, die Teil eines virtuellen privaten Netzwerkes (192.168.127.0/24) sind und jeweils ein eigenes (delegiertes) ZFS-Dateisystem besitzen.

Die Zonen werden über den virtuellen Switch stub127 miteinander verbunden sein:

# dladm create-etherstub stub127

Jede Zone erhält ein eigenes (exklusives) virtuelles Netzwerkinterface (vnic):

# dladm create-vnic -l stub127 vnic11
# dladm create-vnic -l stub127 vnic12
# dladm show-vnic
LINK OVER SPEED MACADDRESS MACADDRTYPE VID
vnic11 stub127 0 2:8:20:9a:cf:f9 random 0
vnic12 stub127 0 2:8:20:d7:18:55 random 0

Die Zonen werden im ZFS rpool/zones laufen und die delegierten Dateisysteme liegen unter rpool/zonefs. Zur Sicherheit bekommt rpool/zonefs noch ein Quota von 5GB:

# zfs create rpool/zones
# zfs create rpool/zonefs
# zfs set quota=5G rpool/zonefs
# zfs create rpool/zonefs/zone1
# zfs create rpool/zonefs/zone2
# zfs list -r rpool/zonefs
NAME         USED AVAIL REFER MOUNTPOINT
rpool/zonefs       94K 5,00G 32K /rpool/zonefs
rpool/zonefs/zone1 31K 5,00G 31K /rpool/zonefs/zone1
rpool/zonefs/zone2 31K 5,00G 31K /rpool/zonefs/zone2

Die Zonenkonfigurationen werden in Textdateien gespeichert …

vi zone1.cfg
create -b
set zonepath=/rpool/zones/zone1
set brand=ipkg
set autoboot=true
set ip-type=exclusive
add net
set physical=vnic11
end
add dataset
set name=rpool/zonefs/zone1
end
vi zone2.cfg
create -b
set zonepath=/rpool/zones/zone2
set brand=ipkg
set autoboot=true
set ip-type=exclusive
add net
set physical=vnic12
end
add dataset
set name=rpool/zonefs/zone2
end

… und zoneadm beim Anlegen der Zonen als Parameter übergeben:

# zonecfg -z zone1 -f zone1.cfg
# zonecfg -z zone2 -f zone2.cfg

Die Installation der Zonen dauert, abhängig von der Internetanbindung, einige Minuten:

# zoneadm -z zone1 install
A ZFS file system has been created for this zone.
 Publisher: Using openindiana.org (http://pkg.openindiana.org/dev/ ).
 Publisher: Using sfe (http://pkg.openindiana.org/sfe/).
 Publisher: Using sfe-encumbered (http://pkg.openindiana.org/sfe-encumbered/).
 Image: Preparing at /rpool/zones/zone1/root.
 Cache: Using /var/pkg/publisher.
Sanity Check: Looking for 'entire' incorporation.
 Installing: Packages (output follows)
 Packages to install: 132
 Create boot environment: No
Create backup boot environment: No
 Services to change: 4

DOWNLOAD PKGS FILES XFER (MB)
Completed 132/132 28252/28252 150.7/150.7

PHASE ACTIONS
Install Phase 40855/40855

PHASE ITEMS
Package State Update Phase 132/132 
Image State Update Phase 2/2

Note: Man pages can be obtained by installing pkg:/system/manual
 Postinstall: Copying SMF seed repository ... done.
 Postinstall: Applying workarounds.
 Done: Installation completed in 226,349 seconds.

Next Steps: Boot the zone, then log into the zone console (zlogin -C)
 to complete the configuration process.
# zoneadm -z zone2 install
[...]

Jetzt stehen zwei installierte, noch nicht konfigurierte Zonen zur Verfügung:

# zoneadm list -vi
ID NAME       STATUS    PATH               BRAND IP 
 0 global     running   /                  ipkg shared
 - zone1      installed /rpool/zones/zone1 ipkg excl 
 - zone2      installed /rpool/zones/zone2 ipkg excl

Die Konfiguration der Zonen wird während des ersten Bootvorganges erledigt. Der Aufruf zlogin -C stellt die hierzu nötig Verbindung zur Konsole der Zone her.

# zoneadm -z zone1 boot
# zlogin -C zone1
# zoneadm -z zone2 boot
# zlogin -C zone2

Um ausserdem vom Hostsystem aus auf das private Zonennetzwerk zugreifen zu können, muss ein weiteres virtuelles Netzwerkinterface erstellt werden und mit dem virtuellen Switch verbunden werden. vnic1 benötigt ausserdem eine IP-Adresse:

# dladm create-vnic -l stub127 vnic1
# ipadm create-addr -T static -a 192.168.127.1/24 vnic1/v4address
# dladm show-vnic
LINK OVER SPEED MACADDRESS MACADDRTYPE VID
vnic11 stub127 0 2:8:20:9a:cf:f9 random 0
vnic12 stub127 0 2:8:20:d7:18:55 random 0
vnic1  stub127 0 2:8:20:80:e2:ff random 0

Fertig sind die Zonen:

# zoneadm list -vi
  ID NAME             STATUS     PATH                           BRAND    IP    
   0 global           running    /                              ipkg     shared
   1 zone1            running    /rpool/zones/zone1             ipkg     excl 
   2 zone2            running    /rpool/zones/zone2             ipkg     excl

Um nicht ständig IP-Adressen tippen zu müssen, kann man die Netzwerknamen dem Hostsystem bekanntmachen …

# vi /etc/hosts
[...]
192.168.127.1   global
192.168.127.11  zone1
192.168.127.12  zone2

… und nötigenfalls in die Zonen kopieren.

# cp /etc/hosts /rpool/zones/zone1/root/etc/
# cp /etc/hosts /rpool/zones/zone2/root/etc/

Wenn das virtuelle Netzwerkinterface im Hostsystem auch nach einem Neustart existieren soll, muss man folgende Datei anlegen:

# vi /etc/hostname.vnic1
192.168.128.1/24

ZFS: Automatische Replikation via ssh

Ich habe das ZYNK-Script von Ben Rockwood dahingehend angepasst, dass es einen ZFS-Dataset vollständig (d. h. rekursiv, inklusive aller abhängigen Dateisysteme und Snapshots) auf einen Backup-Server repliziert. Das Script wird einmal am Tag via crontab aufgerufen, so dass die Daten der letzten Nacht auf dem Backup-Server zur Verfügung stehen. Es kann durchaus häufiger aufgerufen werden (jede Stunde oder auch mehrmals pro Stunde). Hier erst einmal das Skript:

#!/bin/bash
## ZYNK: The Zuper Zimple ZFS Sync (Replication) Tool
## Form: zynk local/dataset root@remote.host destination/dataset

# Please note: The reason this is so simple is because there is no error checking, reporting, or cleanup.
#               In the event that something goes wonkey, you'll manually need to fix the snapshots and
#               modify or remote the /var/run/zynk datafile which contains the most recent snapshot name.
# Furthermore, this absolutely relies on the GNU version of 'date' in order to get epoch time
# Before using, make sure you've distributed your SSH key to the remote host and can ssh without password.

if [ ! $3 ] 
then
        echo "Usage: zynk local/dataset root@remote.host destination/dataset"
        echo "WARNING: The destination is the full path for the remote dataset, not the prefix dataset stub."
        exit
fi

DATE=`date +%s`
if [ $DATE == "%s" ]
then
        echo "Must use GNU Date, please install and modify script."
        exit
fi

if [ -e /var/run/zynk ] 
then
        # Datafile is found, creating incr.
        echo "Incremental started at `date`"
        zfs snapshot -r ${1}@zynk-${DATE}
        zfs send -RI  ${1}@zynk-`cat /var/run/zynk` ${1}@zynk-${DATE} | ssh ${2} zfs recv -Fd ${3}
        zfs destroy -r ${1}@zynk-`cat /var/run/zynk`
        ssh ${2} zfs destroy -r ${3}@zynk-`cat /var/run/zynk`
        echo ${DATE} > /var/run/zynk
        echo "Incremental complete at `date`"
else 
        # Datafile not found, creating full.
        echo "Full started at `date`"
        zfs snapshot -r ${1}@zynk-${DATE}
        zfs send -R     ${1}@zynk-${DATE} | ssh ${2} zfs recv -Fd ${3}
        echo ${DATE} > /var/run/zynk
        echo "Full completed at `date`"
fi

In diesem Szenario soll tank/export vom Server (rubin) nach rpool/export auf dem Backup-Server (onyx) repliziert werden. Beide Systeme laufen mit OpenIndiana build 151a7. Das ZYNK-Skript liegt auf rubin unter /usr/bin/zynk. Der initiale Aufruf sieht wie folgt aus und dauerte hier mehrere Stunden (für 406 GB):

root@rubin:~# rm /var/run/zynk
root@rubin:~# zync tank/export onyx rpool
Full started at 20. November 2012 15:21:09 CET
Full completed at 21. November 2012 04:35:42 CET

Nach diesem ‘Full Backup’ sind die inkrementellen Sicherungen schnell erledigt, da nur noch die Dateisystemänderungen (lies: Snapshots) gesendet werden, die seit dem letzten ZYNK-Aufruf angefallen sind:

root@rubin:~# zynk tank/export onyx rpool
Incremental started at 21. November 2012 13:39:24 CET
Incremental complete at 21. November 2012 13:48:11 CET

Um ZYNK jeden Tag um 2:00 Uhr automatisch aufzurufen, muss schließlich folgende Zeile in die root-crontab (crontab -e) eingetragen werden:

0  2 * * * /usr/bin/zynk tank/export onyx rpool

Das Skript setzt voraus, dass ein passwortloses (ssh-key-basiertes) root-Login möglich ist. Es empfiehlt sich allerdings einen dediziert zynk-Account mit entsprechenden Attributen zu benutzen. Wie schon dem Originalskript von Ben Rockwood, fehlt auch dieser Version jegliche Fehlerprüfung oder -behandlung.

Mountain Lion: Kill the dashboard

Das Dashboard unter Mac OS X ist für mich recht nutzlos. Es behindert allenfalls die Navigation zwischen den virtuellen Desktops und verbraucht überdies wertvolle Resourcen. Es läßt sich relativ einfach deaktivieren, indem folgende Zeile im Terminal (Terminal.app unter Anwendungen/Dienstprogramme) ausgeführt wird:

defaults write com.apple.dashboard mcx-disabled -boolean YES

Beim nächsten anmelden am Mac bleibt das Dashboard weg. Wer nicht solange warten will, kann mit folgendem Kommandozeilenbefehl das Dock neu starten und somit das noch laufende Dashboard beenden:

killall Dock

Nur zur Vollständigkeit: Wer das Dashboard doch vermissen sollte, kann es mit dem Kommandozeilenbefehl

defaults write com.apple.dashboard mcx-disabled -boolean NO

wieder aktivieren.

Das Prozedere sollte für alle Mac OS X Versionen seit 10.4 funktionieren (siehe hier).

OpenIndiana: Umschalten zwischen gdm und Textkonsole

Anders als bei den meisten Linux-Distributionen, ist unter OpenIndiana (OI-151a7) die virtuelle Textkonsole per Default nicht erreichbar (über Ctrl-Alt-F1), wenn gleichzeitig der grafische Login-Manager gdm läuft. Um dieses nützliche Feature zu aktivieren, muss man mittels svccfg die hotkeys-Option des vtdaemon auf true setzen:

svccfg -s vtdaemon setprop options/hotkeys=true

vtdaemon muss selbstverständlich laufen:

svcadm enable -r vtdaemon

Jetzt noch vtdaemon und gdm neustarten:

svcadm refresh vtdaemon
svcadm restart vtdaemon
svcadm restart gdm

Aus der grafischen Konsole kann man jetzt mit  Ctrl-Alt-F1 in die Textkonsole wechseln und mit  (Ctrl-)Alt-F2 auch wieder zurück.

Emacs Twittering-mode: Favs und Zeilenumbruch

Kleines Update zum tw-mode: Zum “faven” eines Tweet, kann man M-x twittering-favorite benutzen (bzw. M-x twittering-unfavorite zum “entfaven”), während sich der Cursor auf dem Tweet in der Timeline befindet.

Wer häufiger favt, kann sich hierfür Kürzel definieren. Die unten stehenden Zeilen müssen hierzu in die ~/.emacs einfügt werden

(let ((km twittering-mode-map))
  (define-key km (kbd "*") 'twittering-favorite)
  (define-key km (kbd "/") 'twittering-unfavorite)
  nil)

um mit den Tasten * und / faven bzw. entfaven zu können.

Um automatisch den Visual-Line-Mode für twittering-mode zu benutzen (z. B. bei kleiner Spaltenbreite) muss zusätzlich

(add-hook 'twittering-mode-hook 'turn-on-visual-line-mode)

in ~/.emacs einfügt werden.

iOS: SMS auf iPhone erzwingen

Um auf dem iPhone den Versand einer Nachricht durch den Short-Message-Service (SMS) zu erzwingen, obwohl iMessage bevorzugt wird (“Senden” wird blau dargestellt), muss man die Nachricht als iMessage versenden und sofort die erscheinende Sprechblase gedrückt halten (Abb. oben, “Nachricht als SMS.”). Hier jetzt “Als Nachricht senden” auswählen und der Text wird via SMS versendet (Abb. unten). Der Versand durch iMessage wird hierdurch abgebrochen.

OpenIndiana: EFI-Label entfernen

Ich wollte heute dem rpool auf einem OpenIndiana-System (151a3) eine weitere Festplatte hinzufügen um einen mirror draus zu machen. Die Festplatte war bereits vorher in einem Raid-Z-Verbund im Einsatz gewesen und hierdurch bereits mit einem EFI-Label versehen:

# fdisk /dev/rdsk/c4d0p0
Total disk size is 60800 cylinders
 Cylinder size is 16065 (512 byte) blocks
Cylinders
 Partition Status Type         Start End Length %
 ========= ====== ============ ===== === ====== ===
     1             EFI            0 60800 60801 100

SELECT ONE OF THE FOLLOWING:
 1. Create a partition
 2. Specify the active partition
 3. Delete a partition
 4. Change between Solaris and Solaris2 Partition IDs
 5. Edit/View extended partitions
 6. Exit (update disk configuration and exit)
 7. Cancel (exit without updating disk configuration)
Enter Selection:

Der erste Fehler den ich gemacht habe, war das ganze Device (z. B. c4d0) hinzufügen zu wollen: Ein root pool muss auf einer Disk mit slices (also z. B. c4d0s0) installiert werden anstatt auf einer ganzen Festplatte (siehe hier):

# zpool attach rpool c5d0 c4d0
cannot label 'c4d0': EFI labeled devices are not supported on root pools.

Der slice c4d0s0 existiert nicht und muss erzeugt werden. Ausserdem kann man Solaris nicht von EFI-Partionen booten, so dass zuvor eine Solaris2-Partition auf der Festplatte erzeugt werden muss:

# fdisk -B /dev/rdsk/c4d0p0

Im nächsten Schritt wird der benötigte slice erzeugt. Zur besseren Übersicht habe ich nach jeder Eingabe einen neuen Code-Block angefangen:

# format
Searching for disks...done

AVAILABLE DISK SELECTIONS:
       0. c4d0
          /pci@0,0/pci-ide@a/ide@0/cmdk@0,0
       1. c5d0
          /pci@0,0/pci-ide@a/ide@1/cmdk@0,0
Specify disk (enter its number): 0
selecting c4d0
Controller working list found
[disk formatted, defect list found]

FORMAT MENU:
        disk       - select a disk
        type       - select (define) a disk type
        partition  - select (define) a partition table
        current    - describe the current disk
        format     - format and analyze the disk
        fdisk      - run the fdisk program
        repair     - repair a defective sector
        show       - translate a disk address
        label      - write label to the disk
        analyze    - surface analysis
        defect     - defect list management
        backup     - search for backup labels
        verify     - read and display labels
        save       - save new disk/partition definitions
        volname    - set 8-character volume name
        !     - execute , then return
        quit
format> p
PARTITION MENU:
        0      - change `0' partition
        1      - change `1' partition
        2      - change `2' partition
        3      - change `3' partition
        4      - change `4' partition
        5      - change `5' partition
        6      - change `6' partition
        7      - change `7' partition
        select - select a predefined table
        modify - modify a predefined partition table
        name   - name the current table
        print  - display the current table
        label  - write partition map and label to the disk
        ! - execute , then return
        quit
partition> modify
Select partitioning base:
        0. Current partition table (original)
        1. All Free Hog
Choose base (enter number) [0]? 1
Part      Tag    Flag     Cylinders         Size            Blocks
  0       root    wm       0                0         (0/0/0)             0
  1       swap    wu       0                0         (0/0/0)             0
  2     backup    wu       0 - 60796      465.73GB    (60797/0/0) 976703805
  3 unassigned    wm       0                0         (0/0/0)             0
  4 unassigned    wm       0                0         (0/0/0)             0
  5 unassigned    wm       0                0         (0/0/0)             0
  6        usr    wm       0                0         (0/0/0)             0
  7 unassigned    wm       0                0         (0/0/0)             0
  8       boot    wu       0 -     0        7.84MB    (1/0/0)         16065
  9 alternates    wm       1 -     2       15.69MB    (2/0/0)         32130

Do you wish to continue creating a new partition
table based on above table[yes]?
Free Hog partition[6]? 0

Die nächsten sechs Eingaben einfach mit (sechs mal) Enter bestätigen.

Enter size of partition '1' [0b, 0c, 0.00mb, 0.00gb]:
Enter size of partition '3' [0b, 0c, 0.00mb, 0.00gb]:
Enter size of partition '4' [0b, 0c, 0.00mb, 0.00gb]:
Enter size of partition '5' [0b, 0c, 0.00mb, 0.00gb]:
Enter size of partition '6' [0b, 0c, 0.00mb, 0.00gb]:
Enter size of partition '7' [0b, 0c, 0.00mb, 0.00gb]:
Part      Tag    Flag     Cylinders         Size            Blocks
  0       root    wm       3 - 60796      465.71GB    (60794/0/0) 976655610
  1       swap    wu       0                0         (0/0/0)             0
  2     backup    wu       0 - 60796      465.73GB    (60797/0/0) 976703805
  3 unassigned    wm       0                0         (0/0/0)             0
  4 unassigned    wm       0                0         (0/0/0)             0
  5 unassigned    wm       0                0         (0/0/0)             0
  6        usr    wm       0                0         (0/0/0)             0
  7 unassigned    wm       0                0         (0/0/0)             0
  8       boot    wu       0 -     0        7.84MB    (1/0/0)         16065
  9 alternates    wm       1 -     2       15.69MB    (2/0/0)         32130

Okay to make this the current partition table[yes]?
Enter table name (remember quotes): "disk0"
Ready to label disk, continue? yes
partition> q
FORMAT MENU:
        disk       - select a disk
        type       - select (define) a disk type
        partition  - select (define) a partition table
        current    - describe the current disk
        format     - format and analyze the disk
        fdisk      - run the fdisk program
        repair     - repair a defective sector
        show       - translate a disk address
        label      - write label to the disk
        analyze    - surface analysis
        defect     - defect list management
        backup     - search for backup labels
        verify     - read and display labels
        save       - save new disk/partition definitions
        volname    - set 8-character volume name
        !     - execute , then return
        quit
format> q

Jetzt kann man das zweite Device zum rpool hinzufügen:

# zpool attach -f rpool c5d0s0 c4d0s0
Make sure to wait until resilver is done before rebooting.

Mit zpool status kann man sich den Fortschritt des Resilver-Prozesses anschauen:

# zpool status
  pool: rpool
 state: ONLINE
status: One or more devices is currently being resilvered.  The pool will
        continue to function, possibly in a degraded state.
action: Wait for the resilver to complete.
  scan: resilver in progress since Thu Apr 26 11:00:36 2012
    6.14G scanned out of 146G at 42.7M/s, 0h55m to go
    6.13G resilvered, 4.20% done
config:

        NAME        STATE     READ WRITE CKSUM
        rpool       ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
            c5d0s0  ONLINE       0     0     0
            c4d0s0  ONLINE       0     0     0  (resilvering)

errors: No known data errors

Nun noch Grub installieren, damit die zweite Platte im Ernstfall auch bootfähig ist:

# installgrub /boot/grub/stage1 /boot/grub/stage2 /dev/rdsk/c4d0s0
stage2 written to partition 0, 275 sectors starting at 50 (abs 16115)
stage1 written to partition 0 sector 0 (abs 16065)