Installer (.pkg oder .mpkg) ohne Dialog (silentmode) von der Konsole unter OS X

Häufig wird Software für OS X als .pkg oder .mpkg Installer-Bundle geliefert (der Unterschied zwischen .mpkg und .pkg ist, daß das .mpkg flexibler hinsichtlich verschiedener Anpassungen ist). Wenn man den Installer durchlaufen lassen möchte, ohne sich dabei durch die GUI-Dialoge klicken zu müssen, dann kann man dies über die Konsole im sogenannten Silentmode tun:

sudo installer -pkg your_installer_file.pkg -target /

Der Installer läuft nun durch, ohne daß eine grafische Benutzeroberfläche aufgeht. Eventuell muß noch das Paßwort des System-Administrators eingegeben werden, sofern der Installer dies benötigt. Das Paßwort kann (aber sollte nicht!) auch in der Kommandozeile mitgegeben werden (so können zum Beispiel automatisiert mehrere Installer ausgeführt werden):

echo <password> | sudo installer -pkg your_installer_file.pkg -target /

||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ACHTUNG: Das Paßwort wird dadurch u.a. in der bash_history gespeichert!

Diese Methode sollte nur verwendet werden, wenn man 100% genau weiß was man dabei tut!

||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

Post to Twitter Tweet This Post

Cluster Filesystem für FreeBSD – GFS, OCFS2, …?

09:45 2 Kommentare

Um es kurz zu machen: Es gibt derzeit absolut KEIN echtes Cluster-Filesystem (wie z.B. GFS oder OCFS2) für FreeBSD. Auch andere Projekte, welche sich mit verteilten Dateisystemen beschäftigen wie z.B. GlusterFS, PVFS oder DRBD sind entweder nicht nach FreeBSD portiert, oder die Portierung ist sehr alt und läuft häufig nicht unter einem aktuellen FreeBSD.
Da ich jedoch vier Filesysteme auf gleichem Stand halten muß (und zwar muß die Aktualisierung binnen Sekunden nach einem Upload erfolgen), habe ich einen kleinen Workaround entwickelt, der dies über rsync und das FreeBSD audit-System ermöglicht. Die Idee das audit-System hierzu zu nutzen habe ich von Luke Marsden, der die Filesystem-Aktivitäten mit audit_control und einigen Python-Scripts überwacht.

Installation & Konfiguration von Event Audit Support

Als erstes muß das audit_system aktiviert und konfiguriert werden. Das event-auditing ist Teil des FreeBSD-Systems selbst und muß im Kernel aktiviert werden.

Folgende Zeile muß in der Kernel-Konfiguration hinzugefügt werden:

options AUDIT

Danach muß der Kernel neu compiliert und installiert werden. Das Vorgehen hierzu ist im FreeBSD Handbook zu finden.

Als nächstes muß die folgende Zeile in der /etc/rc.conf ergänzt werden:

auditd_enable=”YES”

Soweit so gut – nun möchte auch das audit-system selbst eine Konfiguration haben. Hierzu öffnet man die Datei /etc/security/audit_control und ändert die config wie folgt:

dir:/var/audit
flags:fc,fd,fw
minfree:20
naflags:lo
policy:cnt
filesz:0

Das war es schon. Nun kann man das audit-system starten indem man folgenden Befehl ausführt:

/etc/rc.d/auditd start

oder indem man das System via reboot neu startet.

Installation & Aktivierung von rsync

Falls rsync noch nicht auf dem System installiert ist, kann man dies einfach und schnell aus den ports nachholen:

cd /usr/ports/net/rsync
make
make install

Dabei sollte es keine Probleme geben dürfen.

Als nächstes muß nun ein alternativer Pfad zum Daten-Verzeichnis über einen symbolischen Link (“Alias”) erstellt werden; warum dieser benötigt wird werde ich später erklären.

ln -s /path/to/your/data/ /alternative_data_path/

Nun sollte rsync so konfiguriert werden, daß es als daemon läuft. Hierzu erstellen, bzw. verändern wir die Config-Datei /etc/rsyncd.conf wie folgt:

max connections = 5
log file = /var/log/rsync.log
timeout = 30

[shareName]
comment = Name of this “Rsync mount”
path = /alternative_data_path/
read only = no
list = yes
uid = validUser
gid = validGroup
hosts allow = ,
hosts deny = *

Um rsync nun zu starten wird der folgende Befehl ausgeführt:

/usr/local/bin/rsync –config=/etc/rsyncd.conf –daemon

Es ist vermutlich eine gute Idee den rsynd mit einem entsprechenden Tool zu überwachen (z.B. mit den daemontools) damit sichergestellt ist, daß der rsync-Service immer verfügbar ist (in diesem Fall muß rsyncd dann mit der Option –no-detach option gestartet werden).

Das rsync-audit-script

#!/usr/bin/perl
##
# This software is published under the Apchae 2.0 licenses.
# You may obtain a copy of the License at
# 
#   http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#   
#   Author: Erik Scholtz 
#   Web: http://blog.elitecoderz.net
###
# We are strict, cauz we are elitecoderz!
use strict;
use threads qw(yield);
use threads::shared;
use Thread::Semaphore;
 
# No caching
$|=1;
 
################
# Configuration
my $debug = 1;																			# 0/1 to enable logging to the console or disable
my $path = '/path/to/your/data/';				# Path to sync
my @cmds;																				# Syncer commands that should be executed
$cmds[0] = '/usr/local/bin/rsync -raz --progress --size-only /path/to/symboliclink/data/<!--target--> rsync:///shareName/<!--target-->';
$cmds[1] = '/usr/local/bin/rsync -raz --progress --size-only /path/to/symboliclink/data/<!--target--> rsync:///shareName/<!--target-->';
$cmds[2] = '/usr/local/bin/rsync -raz --progress --size-only /path/to/symboliclink/data/<!--target--> rsync:///shareName/<!--target-->';
 
###############################################################################
# DO NOT CHANGE ANYTHING BELOW THIS LINE, UNLESS YOU KNOW WHAT YOU ARE DOING! #
###############################################################################
 
###
# Set Threads yield
threads->yield();
 
# SetUp some thread-shared variables
my $commands :shared;
$commands = &share([]);
 
my $run :shared;
$run = &share({});
$run->{'status'} = 1;
 
my $sema :shared;
$sema = &share({});
 
# Local array where all syncer-threads are stored
my @threads;
 
# Create a thread for each syncer
my $maxid = -1;
for (my $i=0;$i<=$#cmds;$i++) {
	print "Starting syncer $i\n" if $debug;
	$sema->{$i} = Thread::Semaphore->new(0);
	my $syncer = threads->create('syncJob',$run,$sema,$i,$commands,$path,$cmds[$i],$debug);
	push(@threads,$syncer);
	$maxid = $i;
}
 
# Create the Checker thread, which cleanup the jobs and ensures the function of all syncers
$sema->{'checker'} = Thread::Semaphore->new(0);
my $syncer = threads->create('JobChecker',$run,$sema,$commands,$maxid,$debug);
push(@threads,$syncer);
 
# Create the audit thread
print "Starting audit\n" if $debug;
my $auditthread = threads->create('audit',$run,$sema,$commands,$path,$maxid,$debug);
print "Waiting for audi to terminatet\n" if $debug;
$auditthread->join();			# If the audit-thread gets joinable, we have to terminate everything
 
# Terminate all threads and cleanup 
$run->{'status'} = 0;
while ($#threads >=0 ) {
	my $worker = shift(@threads);
	print "Shutdown of syncer ...\n" if $debug;
	$worker->join();
}
print "Shutdown clean completed\n" if $debug;
exit(0);
 
########################################################################################################################################
########################################################################################################################################
 
####################################################################################################
# audit thread
sub audit {
	my $r = shift;
	my $sp = shift;
	my $c = shift;
	my $p = shift;
	my $m = shift;
	my $d = shift;
 
	print "       audit started ...\n" if $d;
	# open listener on the audit device
	open(STATUS, "/usr/sbin/praudit /dev/auditpipe |") || die "can't fork: $!";
	while (<STATUS>) {
		my $line = $_;
		last if ($line eq '' || $r->{'status'}<=0);		# Terminate if audit terminated
		if ($line =~ /path,$p(.+)/) {					# Check if the changed file is in the observed path
			my $file = $1;
			print "Change detected on file: $file\n" if $d;
			my $hash :shared;							# Create a command for the syncers
			$hash = &share({});
			$hash->{'file'} = $file;
			$hash->{'status'} = '';
			$hash->{'time'} = '';
			for (my $j=0;$j<=$m;$j++) {					# init job done charta
				$hash->{$j} = 'no';
			}
			if (1) {
				lock($c);
				push(@{$c},$hash);
				print "Added new job for $file\n" if $d;
			}
			for (my $j=0;$j<=$m;$j++) {					# wakeup syncers
				$sp->{$j}->up();
			}
			$sp->{'checker'}->up();
		}
	}
	close STATUS || die "audit not closed correctly: $! $?";	
	print "       audit terminated ...\n" if $d;
	return(0);
}
 
####################################################################################################
# syncer thread
sub syncJob {
	my $r = shift;
	my $sp = shift;
	my $id = shift;
	my $c = shift;
	my $p = shift;
	my $e = shift;
	my $d = shift;
 
	print "       syncer $id started ...\n" if $d;
 
	while ($r->{'status'}>0) {
		if ($#{$c}>=0) {													# if there are any jobs to be done
			for (my $j=0; $j<=$#{$c}; $j++) {
				next if ($c->[$j]->{$id} eq 'ok');							# if my job is already done skip this job and check next
				my $file = $c->[$j]->{'file'};
				if (-e $p.$file)  {											# check if the file is existing
					$c->[$j]->{$id} = 'working';							# mark this job as being worked on
					my $dif = 1;
					while ($dif>0) {										# check if the file is in upload and changes size within 1,5 secs
						print "Checking Filesize ...\n" if $d;
						my $ssize = -s $p.$file;
						sleep(1.5);
						my $eesize = -s $p.$file;
						$dif = $eesize - $ssize;
						print "Checking Filesize $ssize - $eesize = $dif\n" if $d;
					}
					my $cm = $e;
					$cm =~ s/<!--target-->/$file/g;
					system($cm);											# rsync to other server
				}
				lock($c);
				$c->[$j]->{$id} = 'ok';										# mark job as done for me
			}
		}
		$sp->{$id}->down();
	}
	print "       syncer $id terminated ...\n" if $d;
	return(0);
}
 
####################################################################################################
# checker thread that checks if all jobs are done
sub JobChecker {
	my $r = shift;
	my $sp = shift;
	my $c = shift;
	my $m = shift;
	my $d = shift;
 
	print "       checker started ...\n" if $d;
 
	while ($r->{'status'}>0) {
		while ($#{$c} >= 0 && $r->{'status'}>0) {
			print "    Checker loop ...\n" if $d;
			my $rem = 0;
			foreach my $job (@{$c}) {						# loop through all jobs
				my $mem = 'ok';
				for (my $j=0;$j<=$m;$j++) {					# check job done charta
					if ($job->{$j} eq 'no') {				# job not handled
						$mem = 'no' if ($mem ne 'working');	# job not handled (may never override a job in progress state)
					} elsif ($job->{$j} eq 'working') {		# job in progress (always overrides not handled)
						$mem = 'working';
					}
				}
				# Job not completed
				if ($mem eq 'no') {							
					if ($job->{'time'} eq '') {				# Set timestamp to know, how long this job is already waiting
						$job->{'time'} = time;
					} else {								# Job already got a timestamp
						my $watch = time - $job->{'time'};
						print "Job age: $watch\n" if $d;
						if (time - $job->{'time'} > 300) {	# Job has waited for more than 5 minutes. terminate program
							print "TIME FOR JOB EXCEEDED - shutting down syncer";
							$r->{'status'} = 0;
							for (my $j=0;$j<=$m;$j++) {		# wakeup syncers
								$sp->{$j}->up();
								$sp->{'checker'}->up();		# wakeup ourself
							}
						}
					}
				} elsif ($mem eq 'working') {				# job in progress - just actualize the timestamp
					$job->{'time'} = time;
				} else {
					$job->{'status'} = 'complete';			# job is completely done and is marked for being removed
					$rem = 1;
				}
			}
			# Job to remove available
			if ($rem > 0) {
				lock($c);									# lock the command-queue
				my @arr;
				for (my $j=0;$j<=$#{$c};$j++) {				# store all not handled jobs / drop completed jobs
					my $ex = shift(@{$c});
					if ($ex->{'status'} ne 'complete') {
						push(@arr,$ex);
					}
				}
				for (my $j=0;$j<=$#arr;$j++) {				# put all stored (not finished) jobs back into the command queue
					push(@{$c},$arr[$j]);
				}
			}
			print "    Checker reloop ...\n" if $d;
			sleep(1);
		}
		print "    Checker sleeping (".$#{$c}.")...\n" if $d;
		$sp->{'checker'}->down();
	}
 
	print "       checker terminated ...\n" if $d;
	return(0);
}

Dieses Script ist das Herzstück des Ganzen: Über das audit-system hört es auf Veränderungen von Dateien; bei einer Änderung (oder neu anlegen) einer Datei wird die Datei via rsync direkt auf die anderen Systeme kopiert. Und das ist der Punkt, warum der symbolische Link so wichtig ist. Wenn das Script die Datei via rsync auf ein zweites System kopiert, so registriert das audit-system dort die Änderung und informiert das dort installierte Script. Dieses würde dann direkt die Änderung auf den ersten Server zurück kopieren, wodurch das Script auf dem ersten Server wiederum über eine Änderung informiert würde, usw … Ohne den symbolischen Link würde also eine Endlos-Kopier-Schleife entstehen.

Installation und Konfiguration des Scriptes ist einfach

Das Script wird auf jedes System kopiert, welches mit den jeweils anderen Systemen synchron gehalten werden soll. Ich empfehle dringend auch dieses Script mit den daemontools zu überwachen. Dann muß das Script wie folgt angepasst werden:

$debug kann entweder 0 (keine Debuging Ausgabe) oder 1 (mit Ausgabe) sein.
$path sollte der physikalische Pfad zu den Daten sein.

Für jedes System welches synchronisiert werden soll muß folgende Zeile ergänzt werden. Bitte unbedingt beachten, daß die Nummer in den eckigen Klammern ($cmds["Nummer"]) um jeweils eins erhöht werden muß:

$cmds[0] = ‘/usr/local/bin/rsync -raz –progress –size-only /path/to/symboliclink/data/ rsync:///shareName/‘;

Einige wichtige Informationen zum Schluß:

BEVOR irgendetwas auf dem eigenen System geändert wird, muß unbedingt ein komplettes BackUp erstellt werden. Die Benutzung des Scriptes und des HowTos erfolgt auf eigene Gefahr. Wenn es also aufgrund der Anwendung dieses Scriptes oder Howtos zu irgendwelchen Datenverlusten kommt, dann kann man mich dafür nicht verantwortlich machen.

Um möglichst eine “Echtzeitsynchronisierung” hinzubekommen, startet das Script für jedes System welches synchronisiert werden soll einen eigenen Threas. Deswegen muß die Perl-Installation “thread-enabled” sein.

Post to Twitter Tweet This Post

org.mortbay.thread.Timeout. exception bei neuer GWT webapplication mit instantiations gwt designer oder windowbuilder pro

Wenn man eine neue WebApplication mit der GWT SDK 2.0.3 und der App Engine SDK 1.3.3 in eclipse erstellt und dabei den Instantiations WindowBuilder Pro oder den GWT Designer verwendet, dann taucht der folgende Fehler auf, wenn man versucht die “GWT application” (google nennt dies “Development Mode”) zu starten.

Exception in thread “main” java.lang.NoSuchMethodError: org.mortbay.thread.Timeout.<init>(Ljava/lang/Object;)V
at org.mortbay.io.nio.SelectorManager$SelectSet.<init>(SelectorManager.java:306)
at org.mortbay.io.nio.SelectorManager.doStart(SelectorManager.java:223)
at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)
at org.mortbay.jetty.nio.SelectChannelConnector.doStart(SelectChannelConnector.java:303)
at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)
at org.mortbay.jetty.Server.doStart(Server.java:233)
at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)
at com.google.gwt.dev.shell.jetty.JettyLauncher.start(JettyLauncher.java:543)
at com.google.gwt.dev.DevMode.doStartUpServer(DevMode.java:421)
at com.google.gwt.dev.DevModeBase.startUp(DevModeBase.java:1035)
at com.google.gwt.dev.DevModeBase.run(DevModeBase.java:783)
at com.google.gwt.dev.DevMode.main(DevMode.java:275)

Leider konnte ich bisher nicht herausfinden warum dies passiert, aber ich habe einen Workaround gefunden, mit dem man das Problem verhindern und umgehen kann:

Statt die App Engine SDK 1.3.3 beim Anlegen des neuen Web Application Projektes zu verwenden, erstellt man das Projekt zunächst unter Verwendung der App Engine SDK 1.3.1. Nachdem das Projekt erstellt ist, kann man mit einem Rechts-Klick auf das Projektverzeichnis (in eclipse) die Properties des Projektes ändern: Unter Google>App Engine kann man nun von der App Engine 1.3.1 die Einstellung auf die App Engine 1.3.3 ändern; das Projekt läuft dann ohne Probleme unter der neuen Engine.

select new appengine in the properties

select new appengine in the properties



Post to Twitter Tweet This Post

Missing required argument ‘module[s]‘ Google Web Toolkit – jetty startet nicht in eclipse – project is not GWT project

Missing required argument ‘module[s]‘
Google Web Toolkit 2.0.3
DevMode [-noserver] [-port port-number | "auto"] [-whitelist whitelist-string] [-blacklist blacklist-string] [-logdir directory] [-logLevel level] [-gen dir] [-bindAddress host-name-or-address] [-codeServerPort port-number | "auto"] [-server servletContainerLauncher[:args]] [-startupUrl url] [-war dir] [-extra dir] [-workDir dir] module[s]

where
-noserver Prevents the embedded web server from running
-port Specifies the TCP port for the embedded web server (defaults to 8888)
-whitelist Allows the user to browse URLs that match the specified regexes (comma or space separated)
-blacklist Prevents the user browsing URLs that match the specified regexes (comma or space separated)
-logdir Logs to a file in the given directory, as well as graphically
-logLevel The level of logging detail: ERROR, WARN, INFO, TRACE, DEBUG, SPAM, or ALL
-gen Debugging: causes normally-transient generated types to be saved in the specified directory
-bindAddress Specifies the bind address for the code server and web server (defaults to 127.0.0.1)
-codeServerPort Specifies the TCP port for the code server (defaults to 9997)
-server Specify a different embedded web server to run (must implement ServletContainerLauncher)
-startupUrl Automatically launches the specified URL
-war The directory into which deployable output files will be written (defaults to ‘war’)
-extra The directory into which extra files, not intended for deployment, will be written
-workDir The compiler’s working directory for internal use (must be writeable; defaults to a system temp dir)
and
module[s] Specifies the name(s) of the module(s) to host

Nachdem man einige Zeit mit einem GWT-Projekt in eclipse gearbeitet hat, kann es passieren, daß das Projekt sich nicht mehr als “GWT application” unter Jetty startetn lassen will. Ich versuche derzeit noch herauszufinden was da genau passiert. In der wischenzeit möchte ich kurz erläutern, wie man das Problem umgehen kann:

1) In eclipse vom “Run”-Menü den Menüpunkt “Run Configurations …” auswählen

2) Dann unter “GWT Application” und “Web Application” die Konfigurationen des Projektes löschen

Das war alles. Danach kann man wieder normal aus dem Selektor das Projekt als “GWT Application” starten und es läuft wieder problemlos unter Jetty.

Post to Twitter Tweet This Post

KategorienGWT, eclipse Tags: , ,

iStor Problem gelöst / can’t find block in cyl 0 / cg 0: bad magic number

Nach über einem Jahr Zusammenarbeit mit Danny Braniss in dem wir mehrere tausend Optionen, Einstellungen und Konfigurationen getestet haben, konnte ich nun das iStore iSCSI-device unter FreeBSD zum Laufen bringen.

Zur Erinnerung. Der folgende Fehler trat auf, wenn man versuchte ein UFS Filesystem anzulegen:

newfs -O2 /dev/da0s1
/dev/da0s1: 782023.5MB (1601584044 sectors) block size 16384, fragment size 2048
using 4256 cylinder groups of 183.77MB, 11761 blks, 23552 inodes.
super-block backups (for fsck -b #) at:
160, 376512, 752864, … … …
1601377920
internal error: can’t find block in cyl 0

Im dangerously dedicated mode:

# newfs -O2 /dev/da0

Die Erstellung des Filesystems schlug mit dieser Meldung fehl:

newfs -O2 /dev/da0
/dev/da0: 782023.5MB (1601584044 sectors) block size 16384, fragment size 2048
using 4256 cylinder groups of 183.77MB, 11761 blks, 23552 inodes.
super-block backups (for fsck -b #) at:
160, 376512, 752864, … … …
1601377920
internal error: cg 0: bad magic number

Den entscheidenten Hinweis erhielt ich bei einem Test mit einem PetaStor-System, welches auf Anhieb und ohne Probleme perfekt unter FreeBSD funktionierte. Den letzten Teil des Puzzles fand ich dann in der FreeBSD-FS Mailingliste. Mit den folgenden Befehlen kann das Filesystem geschrieben werden:

# gpart create -s GPT da0"
# gpart show da0
# gpart add -b 34 -s 20971519 -t freebsd-ufs -l AnosLabel da0
# newfs -O2 /dev/da0p1

Wichtiger Hinweis: Die Zahl 20971519 ist durch die device-Größe zu ersetzen, welche einem von gpart show da0 ausgegeben wird.

Post to Twitter Tweet This Post